async & await - polling untuk alternatif [ditutup]


15

Sekarang kita tahu apa yang tersedia untuk c # 5, tampaknya masih ada celah bagi kita untuk memengaruhi pilihan dua kata kunci baru untuk ' Asynchrony ' yang diumumkan oleh Anders Heijsberg kemarin di PDC10 .

async void ArchiveDocuments(List<Url> urls) {
    Task archive = null;
    for(int i = 0; i < urls.Count; ++i) {
        var document = await FetchAsync(urls[i]);
        if (archive != null)
            await archive;
        archive = ArchiveAsync(document);
    }
}

Eric Lippert memiliki penjelasan tentang pilihan dua kata kunci saat ini, dan cara mereka salah mengerti dalam studi kegunaan. Komentar memiliki beberapa proposisi lain.

Tolong - satu saran per jawaban, duplikat akan dihapus.


Ngomong-ngomong "pemrograman asinkron terintegrasi-bahasa" memberi kita LIAP, tidak cukup menggelinding dengan cara yang sama seperti LINQ;)
Benjol

1
Kecuali lompatan yang diucapkan.
Conrad Frix

3
Tapi "Bahasa-Integrasi Asinkron Terintegrasi" akronim dengan baik.
glenatron

Ini harus di luar topik.
DeadMG

Jawaban:


6

Mengingat bahwa saya tidak jelas tentang arti / keperluan async, saya tidak bisa benar-benar membantahnya, tetapi saran terbaik saya untuk mengganti awaitadalah:

yield while (lihat! tidak ada kata kunci baru)

Catatan setelah memikirkan hal ini sedikit lebih banyak, saya bertanya-tanya apakah menggunakan kembaliwhile cara ini adalah ide yang baik - kecenderungan alami akan mengharapkan boolean sesudahnya.

(Berpikir: menemukan kata kunci yang baik seperti mencari nama domain yang bagus :)


+1 dan omong-omong, Anda mengalahkan saya untuk mengomentari entri blog-nya dengan 7 menit ...
Catatan untuk diri sendiri memikirkan nama

Tapi Anda tidak perlu menghasilkan eksekusi jika tugas sudah selesai. Tapi Anda selalu menunggu penyelesaian tugas (meskipun tidak pernah menunggu).
Allon Guralnek

@ Allon Anda tidak harus menjalankan loop body while(x) {...}, jika xsalah.
Catatan untuk diri sendiri - pikirkan nama

@Catatan: Ya tidak ada kata kerja di while. Jika Anda menambahkan kata kerja, seperti do, maka Anda dapat do {...} while (x), yang mengeksekusi tubuh terlepas dari x (setidaknya sekali). Saran Anda yield whiletampaknya sangat mirip do while, tetapi dengan jaminan yang berlawanan untuk melakukan kata kerja, yang mungkin agak menyesatkan (tapi tidak terlalu banyak masalah besar). Hal yang paling tidak saya sukai yieldadalah bahwa itu menyiratkan implementasi suatu mekanisme. Inti dari async/ awaitadalah bahwa Anda menulis operasi asinkron dengan gaya sinkron. yieldmemecah gaya sinkron itu.
Allon Guralnek

Apakah kata kunci baru tentu merupakan hal yang buruk? Seperti yang saya pahami, awaitkata kunci akan dikenali berdasarkan konteks, jadi Anda masih bisa memiliki metode atau variabel bernama "menunggu" jika Anda mau. Untuk beberapa derajat, saya pikir menggunakan kata kunci baru untuk fungsi baru kurang membingungkan daripada menggunakan kembali kata kunci yang ada berarti lebih dari satu hal. (contoh berlebihan: dangermouse.net/esoteric/ook.html )
Tim Goodman

5

Bagaimana kalau tidak memiliki kata kunci?

Saya ingin kompiler menyadari bahwa, sebagian besar waktu, ketika saya memanggil metode asinkron, saya ingin hasilnya.

Document doc = DownloadDocumentAsync();

Itu dia. Alasan orang mengalami kesulitan memikirkan kata kunci untuk hal ini adalah karena itu seperti memiliki kata kunci untuk "lakukan hal yang akan Anda lakukan jika semuanya normal". Itu harus menjadi default, tidak memerlukan kata kunci.

Memperbarui

Saya awalnya menyarankan kompiler harus pintar dengan inferensi tipe untuk mencari tahu apa yang harus dilakukan. Berpikir lebih jauh tentang ini, saya akan menjaga implementasi yang ada di CTP seperti apa adanya, tetapi membuat beberapa tambahan sepele untuk itu, sehingga untuk mengurangi kasus di mana Anda harus menggunakan awaitkata kunci secara eksplisit.

Kami menciptakan sebuah atribut: [AutoAwait]. Ini hanya dapat diterapkan pada metode. Salah satu cara untuk menerapkan ini pada metode Anda adalah menandainya async. Tetapi Anda juga bisa melakukannya dengan tangan, misalnya:

[AutoAwait]
public Task<Document> DownloadDocumentAsync()

Kemudian di dalam asyncmetode apa pun , kompiler akan menganggap Anda ingin menunggu panggilan DownloadDocumentAsync, jadi Anda tidak perlu menentukannya. Panggilan apa pun ke metode itu akan secara otomatis menunggunya.

Document doc = DownloadDocumentAsync();

Sekarang, jika Anda ingin "menjadi pintar" dan memperoleh Task<Document>, Anda menggunakan operator start, yang hanya dapat muncul sebelum pemanggilan metode:

Task<Document> task = start DownloadDocumentAsync();

Rapi, saya pikir. Sekarang pemanggilan metode biasa berarti apa yang biasanya artinya: tunggu sampai metode selesai. Danstart menunjukkan sesuatu yang berbeda: jangan menunggu.

Untuk kode yang muncul di luar asyncmetode, satu-satunya cara Anda diizinkan memanggil [AutoAwait]metode adalah dengan mengawali dengan metode tersebut start. Ini memaksa Anda untuk menulis kode yang memiliki arti yang sama terlepas dari apakah kode itu muncul dalamasync metode atau tidak.

Lalu saya mulai menjadi serakah! :)

Pertama, saya ingin asyncmenerapkan metode antarmuka:

interface IThing
{
    async int GetCount();
} 

Ini pada dasarnya berarti bahwa metode implementasi harus kembali Task<int>atau sesuatu yang kompatibel dengan await, dan penelepon ke metode tersebut akan mendapatkan [AutoAwait]perilaku.

Juga ketika saya menerapkan metode di atas, saya ingin dapat menulis:

async int GetCount()

Jadi saya tidak perlu menyebutkan Task<int> sebagai tipe pengembalian.

Juga, saya ingin asyncmenerapkan tipe delegasi (yang, bagaimanapun juga, seperti antarmuka dengan satu metode). Begitu:

public async delegate TResult AsyncFunc<TResult>();

Seorang asyncdelegasi memiliki - Anda dapat menebaknya - [AutoAwait]perilaku. Dari suatu asyncmetode Anda dapat memanggilnya dan itu akan secara otomatis awaitdiedit (kecuali jika Anda memilih untuk melakukannya saja start). Jadi jika Anda mengatakan:

AsyncFunc<Document> getDoc = DownloadDocumentAsync;

Itu hanya berfungsi. Ini bukan panggilan metode. Belum ada tugas yang dimulai - dan async delegatebukan tugas. Ini pabrik untuk membuat tugas. Anda bisa mengatakan:

Document doc = getDoc();

Dan itu akan memulai tugas dan menunggu sampai selesai dan memberi Anda hasilnya. Atau Anda bisa mengatakan:

Task<Document> t = start getDoc();

Jadi satu tempat di mana "pipa ledeng" bocor adalah bahwa jika Anda ingin membuat delegasi ke suatu asyncmetode, Anda harus tahu untuk menggunakan suatu async delegatetipe. Jadi, bukannya FuncAnda harus mengatakanAsyncFunc , dan seterusnya. Meskipun suatu hari hal semacam itu mungkin diperbaiki oleh inferensi tipe yang ditingkatkan.

Pertanyaan lain adalah apa yang harus terjadi jika Anda mengatakan mulai dengan metode biasa (non-async). Jelas kesalahan kompilasi akan menjadi opsi yang aman. Tetapi ada kemungkinan lain.


Ini mungkin dapat dilakukan dengan konversi implisit tetapi sebaliknya akan memerlukan evaluasi pernyataan dari kiri-ke-kanan (yang persis kebalikan dari bagaimana biasanya kompiler bekerja, kecuali untuk lambdas). Saya pikir saya masih akan menentang ini karena mencegah penggunaan var, berpotensi harus mengganti beberapa jenis nama eksplisit panjang, dan juga ambigu antara awaitkasus dan kasus di mana seseorang secara tidak sengaja menyebut metode async bukan metode sinkron normal. Tampaknya intuitif pada awalnya tetapi sebenarnya melanggar prinsip paling tidak mengejutkan.
Aaronaught

@Aaronaught - Mengapa itu mencegah penggunaan var? Saya ingin tahu apakah Anda merespons revisi sebelumnya atas jawaban saya ... Saya sudah menulis ulang sepenuhnya. Anda sekarang dapat memikirkan saran ini sebagai berikut: jika metode ini ditandai dengan atribut khusus, seolah-olah awaitkata kunci tersebut secara otomatis dimasukkan di depan panggilan ke metode itu (kecuali jika Anda menekan ini dengan startawalan). Semuanya tetap persis seperti dalam CTP, dan karenanya varberfungsi dengan baik.
Daniel Earwicker

Memang saya ... aneh bahwa saya memutuskan untuk mengunjungi kembali utas ini dan menanggapi jawaban Anda pada waktu yang hampir bersamaan ketika Anda memutuskan untuk mengeditnya. Saya harus membaca ulang sekarang ...
Aaronaught

1
Saya suka inversi Anda dari kata kunci yang menunggu. Dan saya juga tidak suka triple-redundansi public async Task<int> FooAsync().
Allon Guralnek

1
Ya, saya melihat bahwa konvensi penamaan post-Async sebagai tanda bahwa sesuatu dapat ditangkap secara lebih formal. Pada dasarnya, jika ada aturan yang mengatakan "metode seperti ini harus dinamai dengan cara tertentu, sehingga orang tahu cara memanggilnya dengan benar" maka aturan yang sama dapat digunakan untuk mengaitkan metode tersebut dengan cara tertentu, dan kemudian kompiler ke membantu Anda memanggil mereka dengan benar.
Daniel Earwicker


4

Saya pikir asyncbaik-baik saja, tapi mungkin itu karena saya mengasosiasikannya dengan halaman ASP.NET async - ide yang sama.

Untuk await kata kunci yang saya suka continue afteratau resume after.

Saya tidak suka yieldatau salah satu variannya, karena semantiknya sedemikian rupa sehingga metodenya mungkin tidak pernah benar-benar menghasilkan eksekusi; itu tergantung pada keadaan tugas.


Saya suka resume afteruntuk await. Mungkin asyncbisa dipanggil resumable.
Tim Goodman

Meskipun saya lebih suka saja after, continue afterpendekatan ini memiliki keunggulan implementasi yang kuat: itu termasuk kata kunci kontekstual yang ada saat ini, tetapi dengan sintaksis yang tidak kompatibel dengan penggunaan saat ini. Itu menjamin bahwa penambahan tidak akan merusak kode yang ada. Saat menggunakan kata kunci yang sama sekali baru, implementasi perlu mengatasi kemungkinan penggunaan kata sebagai pengidentifikasi pada kode lama, yang bisa menjadi sangat rumit.
Edurne Pascual

3

Saya menambahkan komentar di blog Eric juga, saya tidak melihat masalah dengan menggunakan kata kunci yang sama async

var data = async DownloadFileAsync(url);

Saya hanya menyatakan bahwa saya ingin mengunduh file secara tidak sinkron. Ada sedikit redundensi di sini, "async" muncul dua kali, karena itu ada dalam nama metode juga. Compiler dapat menjadi lebih pintar dan mendeteksi konvensi bahwa metode yang diakhiri dengan "Async" adalah metode async yang benar, dan menambahkannya bagi kita dalam kode yang dikompilasi. Jadi, alih-alih Anda mungkin hanya ingin menelepon

var data = async DownloadFile(url);

sebagai lawan memanggil yang sinkron

var data = DownloadFile(url);

Heck, kita juga harus dapat mendefinisikannya dengan cara yang sama, karena kata kunci async ada dalam deklarasi kita, mengapa kita harus menambahkan "Async" secara manual ke setiap nama metode - kompilator dapat melakukannya untuk kita.


Saya suka gula yang Anda tambahkan, meskipun orang-orang C # mungkin tidak akan melakukannya. (FWIW, mereka sudah melakukan hal serupa ketika mencari nama atribut)
Catatan untuk memikirkan sendiri nama

2
Dan mengingat bahwa asynckata kunci pada metode ini hanyalah keramahtamahan (jika saya mengerti dengan benar), saya bertanya-tanya apakah hal terbaik tidak akan melakukan kebalikan dari apa yang Anda sarankan: tinggalkan asyncmetode tersebut, dan gunakan saja di mana mereka saat ini await.
Benjol

Itu kemungkinan. Kata kunci async pada metode penghapusan hanya ada untuk kebersihan sebenarnya. Saya lebih suka disimpan di sana, tetapi tanpa persyaratan bagi kami untuk menambahkan "Async" ke nama metode. Misalnya async Task<Byte[]> DownloadFile(...)alih-alih Task<Byte[]> DownloadFileAsync(...)(Yang terakhir akan menjadi tanda tangan yang dikompilasi). Either way bekerja.
Mark H

Sejujurnya aku bukan penggemar ini. Seperti dalam komentar sebelumnya saya harus menunjukkan bahwa versi final Anda melanggar prinsip paling tidak mengejutkan, karena sebenarnya menggunakan metode yang sama sekali berbeda dari yang ditulis, dan implementasi dan tanda tangan dari metode itu sepenuhnya sampai ke kelas pelaksana . Bahkan versi pertama bermasalah karena tidak benar-benar mengatakan apa-apa (jalankan metode asinkron secara asinkronik ini?). Pikiran yang kami coba ekspresikan adalah kelanjutan atau eksekusi yang ditangguhkan dan ini tidak mengungkapkan itu sama sekali.
Aaronaught

3

async = tugas - Memodifikasi fungsi untuk mengembalikan tugas, jadi mengapa tidak menggunakan kata kunci "tugas"?

await = finish - Kami tidak perlu menunggu, tetapi tugas harus "selesai" sebelum menggunakan hasilnya.


Sangat sulit untuk berdebat dengan kesederhanaan di sini.
sblom

2

Saya suka yield until. yield while, sudah disarankan, bagus dan tidak memperkenalkan kata kunci baru, tapi saya pikir "sampai" menangkap perilaku sedikit lebih baik.

Saya pikir yield <something>ini adalah ide yang hebat, karena hasil sudah menangkap ide membuat sisa metode ini menjadi kelanjutan yang sangat baik. Mungkin seseorang bisa memikirkan kata yang lebih baik daripada "sampai."


2

Saya hanya ingin mendaftarkan suara saya untuk saran Aaron G tentang comefrom- penggunaan yang tepat pertama yang saya lihat dari pernyataan COMEFROM INTERCAL . Idenya adalah bahwa itu kebalikan dari GOTO (melompat dari pernyataan GOTO) karena membuat beberapa tempat dalam kode Anda melompat ke pernyataan COMEFROM.


2

Karena kita berurusan dengan Task<T>s, bagaimana menggunakan startkata kunci sebelum pernyataan, seperti pada:

start var document = FetchAsync(urls[i]);


Hmmm, mungkin finishlebih baik dari itu start?
Protagonis

1

Perlu dicatat bahwa F # juga menggunakan asynckata kunci dalam alur kerja async-nya, yang hampir sama persis dengan fungsi async baru di C # 5. Karena itu, saya akan menjaga yang sama

Untuk awaitkata kunci dalam F #, mereka hanya menggunakan let!bukan let. C # tidak memiliki sintaks tugas yang sama, sehingga mereka membutuhkan sesuatu di sisi kanan =tanda. Seperti yang dikatakan Benjol, fungsinya sama yieldsehingga hampir menjadi varian dari itu.


1
"Menunggu" tidak perlu dalam penugasan sama sekali (meskipun tentu saja biasanya itu.) Adalah sah sebagai operator pada hampir semua ungkapan yang memiliki tipe yang dapat kita temukan di GetAwaiter. (Aturan pasti belum dikerjakan dalam bentuk yang dapat dipublikasikan.)
Eric Lippert

1
@Eric, dalam F # itu akan terjadi do!, tetapi Anda tahu bahwa ...
Benjol

1

yield async FetchAsync(..)


Ini berjalan sempurna dengan asyncpengubah yang Anda butuhkan pada metode yang Anda minta. Dan juga semantik dari saat yield returnini, Anda kembali dan menghasilkan eksekusi ke kode penghitungan sementara dalam hal ini Anda menghasilkan eksekusi Anda ke metode asinkron.

Bayangkan jika di masa depan akan ada kegunaan lain untuk yield, kita bisa menambahkan di yield xmana x adalah fitur baru yang mengkilap alih-alih memiliki semua kata kunci yang berbeda untuk melakukan sebagian besar hal yang sama, menghasilkan eksekusi.

Terus terang, saya tidak begitu mengerti argumen 'tidak menghasilkan eksekusi'. Lagi pula, bukankah titik memanggil metode lain sudah untuk 'menghasilkan eksekusi' ke metode itu? Terlepas dari apakah itu asinkron atau tidak? Saya saya kehilangan sesuatu di sini?

Dan bagus untuk Anda jika asyncpengembaliannya sinkron tetapi memiliki kata kunci di sana harus menandakan bahwa ada kemungkinan metode tersebut akan berjalan secara tidak sinkron dan Anda akan menghasilkan eksekusi ke metode lain. Metode Anda harus menjelaskan hal itu terlepas dari apakah metode itu benar-benar melakukan panggilan tidak sinkron atau tidak.

IMO Saya pikir berbagai kasus 'tidak menghasilkan' adalah detail implementasi. Saya lebih suka menjamin konsistensi dalam bahasa (yaitu menggunakan kembali yield).


0

Bagaimana complete, seperti dalam "Saya ingin tugas selesai"?

Task<byte[]> downloadTask = DownloadFileAsync(url);
byte[] data = complete downloadTask;

1
Mengapa downvote? Adalah rasa hormat untuk setidaknya menjelaskan diri sendiri setelah downvoting.
Allon Guralnek

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.