Kata kunci yang menunggu dalam C # (.NET Async CTP) tidak diizinkan dari dalam pernyataan kunci.
Dari MSDN :
Ekspresi menunggu tidak dapat digunakan dalam fungsi sinkron, dalam ekspresi query, dalam menangkap atau akhirnya blok pernyataan penanganan pengecualian, di blok pernyataan kunci , atau dalam konteks yang tidak aman.
Saya menganggap ini sulit atau tidak mungkin dilakukan oleh tim penyusun karena beberapa alasan.
Saya mencoba menyelesaikan dengan menggunakan pernyataan:
class Async
{
public static async Task<IDisposable> Lock(object obj)
{
while (!Monitor.TryEnter(obj))
await TaskEx.Yield();
return new ExitDisposable(obj);
}
private class ExitDisposable : IDisposable
{
private readonly object obj;
public ExitDisposable(object obj) { this.obj = obj; }
public void Dispose() { Monitor.Exit(this.obj); }
}
}
// example usage
using (await Async.Lock(padlock))
{
await SomethingAsync();
}
Namun ini tidak berfungsi seperti yang diharapkan. Panggilan ke Monitor.Keluar dalam ExitDisposable.Dispose tampaknya memblokir tanpa batas waktu (sebagian besar waktu) menyebabkan kebuntuan sebagai upaya utas lainnya untuk mendapatkan kunci. Saya menduga tidak dapat diandalkannya pekerjaan saya di sekitar dan alasan menunggu pernyataan tidak diizinkan dalam pernyataan kunci entah bagaimana terkait.
Adakah yang tahu mengapa menunggu tidak diizinkan di dalam tubuh pernyataan kunci?
sumber
Jawaban:
Tidak, sama sekali tidak sulit atau tidak mungkin untuk diterapkan - fakta bahwa Anda menerapkannya sendiri adalah bukti dari fakta itu. Sebaliknya, itu adalah ide yang sangat buruk sehingga kami tidak mengizinkannya, untuk melindungi Anda dari melakukan kesalahan ini.
Benar, Anda telah menemukan mengapa kami membuatnya ilegal. Menunggu di dalam kunci adalah resep untuk menghasilkan kebuntuan.
Saya yakin Anda dapat melihat alasannya: kode arbitrer berjalan antara waktu menunggu pengembalian kontrol ke pemanggil dan metode dilanjutkan . Kode arbitrer itu bisa mengeluarkan kunci yang menghasilkan inversi pemesanan kunci, dan karenanya kebuntuan.
Lebih buruk lagi, kode dapat melanjutkan pada utas lain (dalam skenario lanjutan; biasanya Anda mengambil kembali pada utas yang menunggu, tetapi tidak harus) dalam hal itu membuka kunci akan membuka kunci pada utas yang berbeda dari utas yang mengambil keluar kunci. Apakah itu ide yang bagus? Tidak.
Saya perhatikan bahwa ini juga merupakan "praktik terburuk" untuk melakukan bagian
yield return
dalamlock
, karena alasan yang sama. Itu legal untuk melakukannya, tetapi saya berharap kami membuatnya ilegal. Kami tidak akan membuat kesalahan yang sama untuk "menunggu".sumber
SemaphoreSlim.WaitAsync
kelas telah ditambahkan ke .NET framework setelah Anda memposting jawaban ini, saya pikir kita dapat dengan aman berasumsi bahwa itu mungkin sekarang. Terlepas dari ini, komentar Anda tentang sulitnya menerapkan konstruksi semacam itu masih sepenuhnya valid.Gunakan
SemaphoreSlim.WaitAsync
metode.sumber
Stuff
saya tidak melihat jalan keluarnya ...mySemaphoreSlim = new SemaphoreSlim(1, 1)
untuk bekerja sepertilock(...)
?Pada dasarnya itu akan menjadi hal yang salah untuk dilakukan.
Ada dua cara ini bisa diterapkan:
Pegang kunci, hanya lepaskan di ujung blok .
Ini adalah ide yang sangat buruk karena Anda tidak tahu berapa lama operasi asinkron akan berlangsung. Anda hanya boleh memegang kunci untuk waktu yang minimal . Ini juga berpotensi tidak mungkin, karena utas memiliki kunci, bukan metode - dan Anda bahkan tidak dapat menjalankan sisa metode asinkron pada utas yang sama (tergantung pada penjadwal tugas).
Lepaskan kunci di tunggu, dan dapatkan kembali ketika menunggu kembali.
Ini melanggar prinsip IMO yang paling mencengangkan, di mana metode asinkronik harus berperilaku sedekat mungkin seperti kode sinkron yang setara - kecuali jika Anda menggunakan
Monitor.Wait
dalam blok kunci, Anda berharap untuk memiliki kunci selama durasi blok.Jadi pada dasarnya ada dua persyaratan yang bersaing di sini - Anda tidak boleh mencoba melakukan yang pertama di sini, dan jika Anda ingin mengambil pendekatan kedua Anda dapat membuat kode lebih jelas dengan memiliki dua blok kunci yang terpisah dipisahkan oleh ekspresi yang menunggu:
Jadi dengan melarang Anda menunggu di blok kunci itu sendiri, bahasa memaksa Anda untuk berpikir tentang apa yang benar - benar ingin Anda lakukan, dan membuat pilihan itu lebih jelas dalam kode yang Anda tulis.
sumber
SemaphoreSlim.WaitAsync
kelas telah ditambahkan ke .NET framework setelah Anda memposting jawaban ini, saya pikir kita dapat dengan aman berasumsi bahwa itu mungkin sekarang. Terlepas dari ini, komentar Anda tentang sulitnya menerapkan konstruksi semacam itu masih sepenuhnya valid.Ini hanya perpanjangan dari jawaban ini .
Pemakaian:
sumber
try
blok - jika pengecualian terjadi antaraWaitAsync
dantry
semaphore tidak akan pernah dirilis (jalan buntu). Di sisi lain, memindahkanWaitAsync
panggilan ketry
blok akan menimbulkan masalah lain, ketika semaphore dapat dirilis tanpa kunci diperoleh. Lihat utas terkait di mana masalah ini dijelaskan: stackoverflow.com/a/61806749/7889645Ini merujuk ke http://blogs.msdn.com/b/pfxteam/archive/2012/02/12/10266988.aspx , http://winrtstoragehelper.codeplex.com/ , toko aplikasi Windows 8 dan .net 4.5
Inilah sudut pandang saya tentang ini:
Fitur bahasa async / menunggu membuat banyak hal cukup mudah tetapi juga memperkenalkan skenario yang jarang ditemui sebelum begitu mudah untuk menggunakan panggilan async: reentrance.
Ini terutama berlaku untuk event handler, karena untuk banyak event Anda tidak memiliki petunjuk tentang apa yang terjadi setelah Anda kembali dari event handler. Satu hal yang mungkin benar-benar terjadi adalah, bahwa metode async yang Anda tunggu di event handler pertama, dipanggil dari event handler lain yang masih berada di utas yang sama.
Berikut ini adalah skenario nyata yang saya temui di aplikasi windows 8 App store: Aplikasi saya memiliki dua bingkai: masuk dan keluar dari bingkai saya ingin memuat / menyimpan beberapa data ke file / penyimpanan. Acara OnNavigatedTo / From digunakan untuk menyimpan dan memuat. Penghematan dan pemuatan dilakukan oleh beberapa fungsi utilitas async (seperti http://winrtstoragehelper.codeplex.com/ ). Ketika menavigasi dari frame 1 ke frame 2 atau ke arah lain, beban async dan operasi yang aman dipanggil dan ditunggu. Penangan event menjadi async dengan mengembalikan batal => mereka tidak bisa ditunggu.
Namun, operasi buka file pertama (katakanlah: di dalam fungsi simpan) dari utilitas juga async dan sehingga yang pertama menunggu mengembalikan kontrol ke framework, yang beberapa waktu kemudian memanggil utilitas lain (memuat) melalui event handler kedua. Pemuatan sekarang mencoba untuk membuka file yang sama dan jika file tersebut dibuka sekarang untuk operasi penyimpanan, gagal dengan pengecualian TERAKSESENSI.
Solusi minimum bagi saya adalah mengamankan akses file melalui penggunaan dan AsyncLock.
Harap dicatat bahwa kuncinya pada dasarnya mengunci semua operasi file untuk utilitas hanya dengan satu kunci, yang tidak perlu kuat tetapi berfungsi dengan baik untuk skenario saya.
Inilah proyek pengujian saya: aplikasi toko aplikasi windows 8 dengan beberapa panggilan uji coba untuk versi asli dari http://winrtstoragehelper.codeplex.com/ dan versi modifikasi saya yang menggunakan AsyncLock dari Stephen Toub http: //blogs.msdn. com / b / pfxteam / arsip / 2012/02/12 / 10266988.aspx .
Bolehkah saya juga menyarankan tautan ini: http://www.hanselman.com/blog/ComparingTwoTechniquesInNETAsynchronousCoordinationPrimitive.aspx
sumber
Stephen Taub telah menerapkan solusi untuk pertanyaan ini, lihat Membangun Async Coordination Primitives, Bagian 7: AsyncReaderWriterLock .
Stephen Taub sangat dihormati di industri ini, jadi apa pun yang ditulisnya mungkin solid.
Saya tidak akan mereproduksi kode yang dia posting di blog-nya, tetapi saya akan menunjukkan kepada Anda bagaimana menggunakannya:
Jika Anda menginginkan metode yang dimasukkan ke dalam kerangka .NET, gunakan
SemaphoreSlim.WaitAsync
saja. Anda tidak akan mendapatkan kunci pembaca / penulis, tetapi Anda akan dicoba dan diuji implementasi.sumber
SemaphoreSlim.WaitAsync
dalam kerangka NET. Semua kode ini dilakukan adalah menambahkan konsep kunci pembaca / penulis.Hmm, terlihat jelek, sepertinya berhasil.
sumber
Saya memang mencoba menggunakan Monitor (kode di bawah) yang tampaknya berfungsi tetapi memiliki GOTCHA ... ketika Anda memiliki beberapa utas akan memberikan ... System.Threading.SynchronizationLockException Metode sinkronisasi objek dipanggil dari blok kode yang tidak disinkronkan.
Sebelum ini saya hanya melakukan ini, tapi itu di controller ASP.NET sehingga mengakibatkan kebuntuan.
public async Task<FooResponse> ModifyFooAsync() { lock(lockObject) { return SomeFunctionToModifyFooAsync.Result; } }
sumber