Bagaimana dan kapan menggunakan 'async' dan 'menunggu'

1066

Dari pemahaman saya salah satu hal utama yang asyncdanawait lakukan adalah untuk membuat kode mudah untuk menulis dan membaca - tetapi menggunakan mereka sama untuk pemijahan benang latar belakang untuk melakukan logika durasi panjang?

Saat ini saya sedang mencoba contoh paling dasar. Saya telah menambahkan beberapa komentar sebaris. Bisakah Anda menjelaskannya untuk saya?

// I don't understand why this method must be marked as `async`.
private async void button1_Click(object sender, EventArgs e)
{
    Task<int> access = DoSomethingAsync();
    // task independent stuff here

    // this line is reached after the 5 seconds sleep from 
    // DoSomethingAsync() method. Shouldn't it be reached immediately? 
    int a = 1; 

    // from my understanding the waiting should be done here.
    int x = await access; 
}

async Task<int> DoSomethingAsync()
{
    // is this executed on a background thread?
    System.Threading.Thread.Sleep(5000);
    return 1;
}
Dan Dinu
sumber
48
Juga, dalam contoh Anda perhatikan bahwa Anda mendapat peringatan ketika Anda menyusun kode di atas. Perhatikan peringatan itu . Memberitahu Anda bahwa kode ini tidak masuk akal.
Eric Lippert

Jawaban:

759

Saat menggunakan asyncdan awaitkompiler menghasilkan mesin status di latar belakang.

Berikut adalah contoh yang saya harap saya bisa menjelaskan beberapa detail tingkat tinggi yang sedang terjadi:

public async Task MyMethodAsync()
{
    Task<int> longRunningTask = LongRunningOperationAsync();
    // independent work which doesn't need the result of LongRunningOperationAsync can be done here

    //and now we call await on the task 
    int result = await longRunningTask;
    //use the result 
    Console.WriteLine(result);
}

public async Task<int> LongRunningOperationAsync() // assume we return an int from this long running operation 
{
    await Task.Delay(1000); // 1 second delay
    return 1;
}

Oke, jadi apa yang terjadi di sini:

  1. Task<int> longRunningTask = LongRunningOperationAsync(); mulai mengeksekusi LongRunningOperation

  2. Pekerjaan independen dilakukan pada mari kita asumsikan Utas Utama (Utas ID = 1) kemudian await longRunningTasktercapai.

    Sekarang, jika longRunningTaskbelum selesai dan masih berjalan, MyMethodAsync()akan kembali ke metode pemanggilannya, sehingga utas utama tidak diblokir. Ketika longRunningTaskselesai maka utas dari ThreadPool (dapat berupa utas apa pun) akan kembali ke MyMethodAsync()dalam konteks sebelumnya dan melanjutkan eksekusi (dalam hal ini mencetak hasilnya ke konsol).

Kasus kedua adalah bahwa longRunningTasktelah menyelesaikan eksekusi dan hasilnya tersedia. Ketika mencapai await longRunningTaskkita sudah memiliki hasil sehingga kode akan terus dieksekusi pada utas yang sama. (dalam hal ini hasil cetak ke konsol). Tentu saja ini bukan kasus untuk contoh di atas, di mana ada yang Task.Delay(1000)terlibat.

Dan Dinu
sumber
65
Mengapa kita memiliki "menunggu" dengan "Task.Delay (1000);" dalam metode async LongRunningOperation?
Benison Sam
3
@codea Dalam komentar Eric Lippert untuk artikel yang ia tautkan dengan artikel pengantar untuk topik ini di mana ia secara khusus membandingkan strategi DoEvents dengan async-waiting
Camilo Martinez
13
@BenisonSam utas agak lama, tapi saya punya pertanyaan yang sama dan telah mencari jawaban. Alasan untuk "menunggu" adalah bahwa jika kita mengabaikan "tunggu", LongRunningOperationAsync () akan segera kembali. Bahkan kompiler akan memberikan peringatan jika kita menghapus menunggu. Posting blog Stephen Cleary blog.stephencleary.com/2011/09/… memberikan yang terbaik dari diskusi desain.
shelbypereira
70
Jika setiap metode async perlu menunggu di dalamnya, dan menunggu hanya dapat dilakukan pada metode dengan async, kapan itu berhenti?
Bruno Santos
108
Jawaban ini jelas salah. Banyaknya upvotes ini akan menyebabkan pemahaman yang salah bagi banyak pengguna. Dokumentasi MS dengan jelas mengatakan, tidak ada utas lain yang digunakan ketika hanya menggunakan async, tunggu. msdn.microsoft.com/en-us/library/mt674882.aspx Tolong ada yang memperbaiki jawabannya. Karena ini saya membuang satu hari penuh.
Krishna Deepak
171

Dari pemahaman saya salah satu hal utama yang async dan tunggu lakukan adalah membuat kode mudah untuk ditulis dan dibaca.

Mereka membuat kode asinkron mudah ditulis dan dibaca, ya.

Apakah ini sama dengan pemunculan utas latar untuk melakukan logika durasi panjang?

Tidak semuanya.

// Saya tidak mengerti mengapa metode ini harus ditandai sebagai 'async'.

Kata asynckunci memungkinkan awaitkata kunci. Jadi metode apa pun yang digunakan awaitharus ditandai async.

// Baris ini tercapai setelah 5 detik tidur dari metode DoSomethingAsync (). Bukankah itu harus segera dicapai?

Tidak, karena asyncmetode tidak dijalankan pada utas lainnya secara default.

// Apakah ini dieksekusi pada utas latar belakang?

Tidak.


Anda mungkin menemukan async/ awaitintro saya bermanfaat. Dokumen resmi MSDN juga luar biasa baik (khususnya bagian TAP ), dan asynctim mengeluarkan FAQ yang sangat baik .

Stephen Cleary
sumber
6
Jadi itu tidak berjalan di utas latar belakang, tetapi juga tidak memblokir. Ini dimungkinkan karena API asinkron yang menggunakan callback alih-alih menyulapnya dengan utas. Anda memulai operasi (I / O, socket, ..) dan kembali melakukan hal-hal Anda. Ketika operasi selesai, OS akan memanggil panggilan balik. Ini adalah apa yang Node.js atau kerangka Python Twisted lakukan dan mereka memiliki beberapa penjelasan yang bagus juga.
Roman Plášil
3
"Kata kunci async memungkinkan kata kunci yang menunggu. Jadi metode apa pun yang menggunakan harus ditandai sebagai async.", - tapi mengapa? jawaban ini tidak membantu untuk memahami mengapa metode ini harus ditandai sebagai async. Tidak bisakah kompiler hanya menyimpulkan bahwa metode ini async dengan melihat ke dalam untuk menunggu kata kunci?
Stanislav
9
@Stanislav: Saya punya entri blog yang membahas pertanyaan itu.
Stephen Cleary
3
Klarifikasi yang disarankan: Tidak, karena asyncmetode tidak dijalankan pada utas lainnya secara default. Dalam contoh Anda, Sleep()panggilan di dalam DoSomethingAsync()memblokir utas saat ini yang mencegah eksekusi dari melanjutkan button1_Click()hingga DoSomethingAsync()selesai. Perhatikan bahwa saat Thread.Sleep()memblokir utas pelaksana,Task.Delay() does not.
DavidRR
166

Penjelasan

Berikut ini adalah contoh cepat dari async/ awaitdi tingkat tinggi. Ada lebih banyak detail untuk dipertimbangkan di luar ini.

Catatan: Task.Delay(1000)disimulasikan melakukan pekerjaan selama 1 detik. Saya pikir yang terbaik adalah memikirkan ini sebagai menunggu tanggapan dari sumber eksternal. Karena kode kami menunggu respons, sistem dapat mengatur tugas yang sedang berjalan ke samping dan kembali ke sana setelah selesai. Sementara itu, ia dapat melakukan beberapa pekerjaan lain pada utas itu.

Pada contoh di bawah ini, blok pertama melakukan hal itu. Ini memulai semua tugas segera ( Task.Delaygaris) dan menetapkannya ke samping. Kode akan berhenti di await abaris sampai penundaan 1 detik dilakukan sebelum pergi ke baris berikutnya. Sejak b, c, d, dan esemua mulai melaksanakan pada waktu yang hampir sama persis seperti a(karena kurangnya menunggu), mereka harus menyelesaikan di sekitar waktu yang sama dalam hal ini.

Dalam contoh di bawah ini, blok kedua memulai tugas dan menunggu sampai selesai (itulah yang awaitdilakukan) sebelum memulai tugas berikutnya. Setiap iterasi ini membutuhkan waktu 1 detik. Ini awaitsedang menjeda program dan menunggu hasilnya sebelum melanjutkan. Ini adalah perbedaan utama antara blok pertama dan kedua.

Contoh

Console.WriteLine(DateTime.Now);

// This block takes 1 second to run because all
// 5 tasks are running simultaneously
{
    var a = Task.Delay(1000);
    var b = Task.Delay(1000);
    var c = Task.Delay(1000);
    var d = Task.Delay(1000);
    var e = Task.Delay(1000);

    await a;
    await b;
    await c;
    await d;
    await e;
}

Console.WriteLine(DateTime.Now);

// This block takes 5 seconds to run because each "await"
// pauses the code until the task finishes
{
    await Task.Delay(1000);
    await Task.Delay(1000);
    await Task.Delay(1000);
    await Task.Delay(1000);
    await Task.Delay(1000);
}
Console.WriteLine(DateTime.Now);

KELUARAN:

5/24/2017 2:22:50 PM
5/24/2017 2:22:51 PM (First block took 1 second)
5/24/2017 2:22:56 PM (Second block took 5 seconds)

Info tambahan tentang SynchronizationContext

Catatan: Di sinilah segalanya menjadi sedikit berkabut bagi saya, jadi jika saya salah dalam hal apa pun, perbaiki saya dan saya akan memperbarui jawabannya. Sangat penting untuk memiliki pemahaman dasar tentang bagaimana ini bekerja tetapi Anda bisa bertahan tanpa menjadi ahli selama Anda tidak pernah menggunakannya ConfigureAwait(false), meskipun Anda mungkin akan kehilangan beberapa peluang untuk optimasi, saya berasumsi.

Ada satu aspek ini yang membuat async/ awaitkonsep yang agak rumit untuk dipahami. Itulah fakta bahwa dalam contoh ini, ini semua terjadi pada utas yang sama (atau setidaknya yang tampaknya merupakan utas yang sama terkait dengan utasnya SynchronizationContext). Secara default, awaitakan mengembalikan konteks sinkronisasi dari utas asli yang sedang berjalan. Misalnya, di ASP.NET Anda memiliki HttpContextyang terkait dengan utas saat permintaan masuk. Konteks ini berisi hal-hal khusus untuk permintaan Http asli seperti objek Permintaan asli yang memiliki hal-hal seperti bahasa, alamat IP, header, dll. Jika Anda mengganti utas saat memproses sesuatu, Anda mungkin berpotensi mencoba menarik informasi dari objek ini pada objek lainHttpContextyang bisa menjadi bencana. Jika Anda tahu Anda tidak akan menggunakan konteks untuk apa pun, Anda dapat memilih untuk "tidak peduli" tentang hal itu. Ini pada dasarnya memungkinkan kode Anda dijalankan pada utas terpisah tanpa membawa konteksnya.

Bagaimana Anda mencapai ini? Secara default, await a;kode sebenarnya membuat asumsi bahwa Anda DO ingin menangkap dan memulihkan konteks:

await a; //Same as the line below
await a.ConfigureAwait(true);

Jika Anda ingin memperbolehkan kode utama untuk melanjutkan pada utas baru tanpa konteks asli, Anda cukup menggunakan false dan bukan true sehingga ia tahu itu tidak perlu mengembalikan konteks.

await a.ConfigureAwait(false);

Setelah program dihentikan sementara, program akan terus berpotensi pada utas yang sama sekali berbeda dengan konteks yang berbeda. Di sinilah peningkatan kinerja akan berasal - ini dapat berlanjut pada utas yang tersedia tanpa harus mengembalikan konteks aslinya.

Apakah ini membingungkan? Tentu saja! Bisakah Anda mencari tahu? Mungkin! Setelah Anda memiliki pemahaman konsep, kemudian beralih ke penjelasan Stephen Cleary yang cenderung lebih diarahkan pada seseorang dengan pemahaman teknis tentang async/ awaitsudah.

Joe Phillips
sumber
Katakanlah jika semua tugas ini mengembalikan int dan jika saya menggunakan hasil tugas pertama di tugas kedua (atau beberapa perhitungan) Apakah akan salah?
veerendra gupta
3
@veerendragupta ya. Anda akan dengan sadar memilih untuk tidak menjalankannya secara tidak sinkron dalam kasus itu (karena mereka tidak sinkron). Ada juga beberapa hal yang perlu saya sadari terkait konteks konfigurasi yang tidak akan saya
Joe Phillips
Jadi, await MethodCall()apakah benar-benar pemborosan? Anda mungkin juga menjatuhkan await/ async?
Vitani
2
@ Joseph Tidak cukup. Ketika Anda menelepon await, saya pikir itu melepaskan utas kembali ke kolam daripada menahannya. Ini membuatnya tersedia untuk digunakan di tempat lain sambil menunggu kembalinya Tugas
Joe Phillips
2
@ JoPhillips Saya pikir apa yang baru saja Anda katakan adalah inti dari async / tunggu. Thread panggilan dibebaskan dan dapat digunakan oleh proses lain pada mesin. Ketika panggilan tunggu selesai, utas baru digunakan untuk melanjutkan apa yang awalnya dimulai oleh penelepon. Penelepon masih menunggu, tetapi manfaatnya adalah utas dibebaskan sementara itu. Itulah manfaat async / tunggu?
Bob Horn
147

Lebih jauh ke jawaban lain, lihat menunggu (C # Referensi)

dan lebih khusus pada contoh yang disertakan, ini menjelaskan situasi Anda sedikit

Contoh Windows Forms berikut menggambarkan penggunaan menunggu dalam metode async, WaitAsynchronouslyAsync. Bandingkan perilaku metode itu dengan perilaku WaitSynchronously. Tanpa menunggu operator diterapkan pada tugas, WaitSynchronously berjalan secara sinkron meskipun menggunakan pengubah async dalam definisi dan panggilan untuk Thread. Tidur dalam tubuhnya.

private async void button1_Click(object sender, EventArgs e)
{
    // Call the method that runs asynchronously.
    string result = await WaitAsynchronouslyAsync();

    // Call the method that runs synchronously.
    //string result = await WaitSynchronously ();

    // Display the result.
    textBox1.Text += result;
}

// The following method runs asynchronously. The UI thread is not
// blocked during the delay. You can move or resize the Form1 window 
// while Task.Delay is running.
public async Task<string> WaitAsynchronouslyAsync()
{
    await Task.Delay(10000);
    return "Finished";
}

// The following method runs synchronously, despite the use of async.
// You cannot move or resize the Form1 window while Thread.Sleep
// is running because the UI thread is blocked.
public async Task<string> WaitSynchronously()
{
    // Add a using directive for System.Threading.
    Thread.Sleep(10000);
    return "Finished";
}
Adriaan Stander
sumber
3
Terima kasih atas jawabannya. Tetapi apakah WaitAsynchronouslyAsync () dijalankan pada utas terpisah?
Dan Dinu
32
Saya percaya begitu, dari bagian Ekspresi menunggu tidak memblokir utas yang sedang dieksekusi. Sebaliknya, itu menyebabkan kompiler untuk mendaftar sisa metode async sebagai kelanjutan dari tugas yang ditunggu. Kontrol kemudian kembali ke pemanggil metode async. Ketika tugas selesai, ia memanggil kelanjutannya, dan eksekusi metode async dilanjutkan di tempat yang sebelumnya tidak digunakan.
Adriaan Stander
13
Menurut artikel MSDN ini , "Async dan menunggu kata kunci tidak menyebabkan utas tambahan dibuat .... metode async tidak berjalan pada utasnya sendiri". Pemahaman saya adalah bahwa menunggu kata kunci kerangka melompat ke depan (kembali ke penelepon) untuk memungkinkan semua kode independen untuk menjalankan sambil menunggu operasi panjang selesai. Saya pikir itu berarti bahwa setelah semua kode independen telah berjalan, jika operasi lama belum kembali, itu akan diblokir. Saya hanya belajar ini sekarang.
Vimes
9
@ASTander Itu tidak benar. Itu tidak mengeksekusi di utas yang berbeda. Itu hanya menjadwalkan kelanjutan (sisa metode) untuk dipanggil ketika timer digunakan oleh Task.Delayapi.
MgSam
1
Jawaban ini salah karena Tidur. Lihat jawaban yang diterima dengan menunggu Task.Delay (1000); yang memiliki perilaku yang benar.
Jared Updike
62

Menampilkan penjelasan di atas dalam aksi dalam program konsol sederhana:

class Program
{
    static void Main(string[] args)
    {
        TestAsyncAwaitMethods();
        Console.WriteLine("Press any key to exit...");
        Console.ReadLine();
    }

    public async static void TestAsyncAwaitMethods()
    {
        await LongRunningMethod();
    }

    public static async Task<int> LongRunningMethod()
    {
        Console.WriteLine("Starting Long Running method...");
        await Task.Delay(5000);
        Console.WriteLine("End Long Running method...");
        return 1;
    }
}

Dan hasilnya adalah:

Starting Long Running method...
Press any key to exit...
End Long Running method...

Jadi,

  1. Main memulai metode long running via TestAsyncAwaitMethods. Itu segera kembali tanpa menghentikan utas saat ini dan kami segera melihat pesan 'Tekan sembarang tombol untuk keluar'
  2. Semua ini sementara, LongRunningMethodberjalan di latar belakang. Setelah selesai, utas lain dari Threadpool mengambil konteks ini dan menampilkan pesan terakhir

Jadi, bukan utas yang diblokir.

sppc42
sumber
"Tekan tombol apa saja untuk keluar ..." akan ditampilkan di bagian output mana?
StudioX
1
dan apa gunanya (return 1)? Apakah itu perlu?
StudioX
1
@StudioX saya pikir itu harus memiliki tipe integer kembali
Kuba Do
Saya pikir return 1bagian ini layak mendapatkan beberapa penjelasan lebih lanjut: awaitkata kunci memungkinkan Anda untuk mengembalikan jenis yang mendasarinya Task<T>secara langsung, sehingga memudahkan untuk menyesuaikan kode Anda yang ada dengan dunia menunggu / async . Tetapi Anda tidak harus mengembalikan nilai, karena dimungkinkan untuk mengembalikan Tasktanpa menentukan jenis pengembalian, yang akan setara dengan voidmetode sinkron . Ingatlah bahwa C # memungkinkan async voidmetode, tetapi Anda harus menghindari melakukannya kecuali jika Anda menangani penangan acara.
Christiano Kiss
41

Saya pikir Anda telah mengambil contoh yang buruk dengan System.Threading.Thread.Sleep

Titik asyncTugas adalah membiarkannya dieksekusi di latar belakang tanpa mengunci utas utama, seperti melakukan aDownloadFileAsync

System.Threading.Thread.Sleep bukan sesuatu yang "sedang dikerjakan", itu hanya tidur, dan karena itu baris Anda berikutnya tercapai setelah 5 detik ...

Baca artikel ini, saya pikir ini adalah penjelasan asyncdan awaitkonsep yang hebat: http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx

Vnuk
sumber
3
Mengapa Tidur adalah contoh yang buruk tetapi Unduh adalah contoh yang baik. Ini seperti FooBar hal ketika saya melihat Thread. Tidur saya mengerti ada beberapa tugas yang membutuhkan waktu. Saya pikir pertanyaannya relevan
Abdurrahim
1
@Abdurrahim Thread.Sleepmemblokir utas (utas tidak dapat melakukan hal lain selain duduk diam), tetapi metode async tidak. Dalam hal ini DownloadFileAsync, utas dapat pergi dan melakukan sesuatu yang lain sampai balasan datang dari server jauh. Tempat penampung yang lebih baik untuk "beberapa tugas yang membutuhkan waktu" dalam metode async adalah Task.Delay, karena itu sebenarnya asinkron.
Gabriel Luci
@GabrielLuci keberatan saya bukan tentang Keterlambatan vs Tidur; Jawaban Anda lebih mirip dengan jawaban stroberi; Jika Anda menempatkan ini sebagai komentar untuk pertanyaan yang bukan apa-apa, saya bisa menolak, tetapi sebagai jawaban, baunya lebih seperti jawaban stroberi. Saya pikir masih baik untuk menggunakan async di sana bahkan semua panggilan yang harus dibuatnya akan memblokir panggilan; Itu tidak akan membatalkan semua tujuan ... Bahkan semua yang tersisa akan menjadi gula sintaksis yang dianggap sebagai kasus yang valid,
Abdurrahim
1
Ini bukan jawaban saya. Tetapi untuk menjawab maksud Anda: itu tergantung pada tujuan metode. Jika dia hanya ingin metode untuk memanggil, dia berhasil. Namun dalam hal ini ia berusaha membuat metode yang berjalan secara tidak sinkron. Dia melakukannya dengan hanya menggunakan asynckata kunci. Tetapi metodenya masih berjalan secara sinkron, dan jawaban ini menjelaskan mengapa: karena dia tidak benar-benar menjalankan kode asinkron. Metode yang ditandai asyncmasih berjalan secara sinkron hingga Anda awaittidak lengkap Task. Jika tidak ada await, maka metode berjalan secara sinkron, dan kompiler akan memperingatkan Anda tentang hal itu.
Gabriel Luci
23

Berikut adalah program konsol cepat untuk menjelaskannya kepada mereka yang mengikuti. The TaskToDometode adalah metode berjalan lama Anda bahwa Anda ingin membuat async. Menjalankan async dilakukan dengan TestAsyncmetode ini. Metode loop tes hanya menjalankan TaskToDotugas dan menjalankannya async. Anda dapat melihat bahwa di hasil karena mereka tidak menyelesaikan dalam urutan yang sama dari menjalankan ke menjalankan - mereka melaporkan ke utas UI konsol ketika mereka selesai. Sederhana, tapi saya pikir contoh sederhana menunjukkan inti dari pola lebih baik daripada contoh yang lebih terlibat:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace TestingAsync
{
    class Program
    {
        static void Main(string[] args)
        {
            TestLoops();
            Console.Read();
        }

        private static async void TestLoops()
        {
            for (int i = 0; i < 100; i++)
            {
                await TestAsync(i);
            }
        }

        private static Task TestAsync(int i)
        {
            return Task.Run(() => TaskToDo(i));
        }

        private async static void TaskToDo(int i)
        {
            await Task.Delay(10);
            Console.WriteLine(i);
        }
    }
}
MarkWalls
sumber
22

Untuk pembelajaran tercepat ..

  • Memahami aliran eksekusi metode (dengan diagram): 3 menit

  • Introspeksi pertanyaan (sake pembelajaran): 1 menit

  • Cepat melewati sintaks gula: 5 menit

  • Bagikan kebingungan pengembang: 5 menit

  • Masalah: Cepat ubah implementasi kode normal dunia nyata menjadi kode Async: 2 menit

  • Kemana Selanjutnya?

Memahami aliran eksekusi metode (dengan diagram): 3 menit

Dalam gambar ini, fokus saja pada # 6 (tidak lebih) masukkan deskripsi gambar di sini

Pada langkah # 6: Eksekusi berhenti di sini karena kehabisan pekerjaan. Untuk melanjutkannya, dibutuhkan hasil dari getStringTask (semacam fungsi). Oleh karena itu, ia menggunakan awaitoperator untuk menunda progresnya dan memberikan kendali kembali (hasil) kepada penelepon (dari metode yang kita gunakan ini). Panggilan sebenarnya ke getStringTask dibuat lebih awal di # 2. Di # 2 janji dibuat untuk mengembalikan hasil string. Tetapi kapan akan mengembalikan hasilnya? Haruskah kita (# 1: AccessTheWebAsync) melakukan panggilan kedua lagi? Siapa yang mendapatkan hasilnya, # 2 (pernyataan panggilan) atau # 6 (menunggu pernyataan)

Penelepon eksternal AccessTheWebAsync () juga sedang menunggu sekarang. Jadi pemanggil menunggu AccessTheWebAsync, dan AccessTheWebAsync sedang menunggu GetStringAsync saat ini. Yang menarik adalah AccessTheWebAsync melakukan beberapa pekerjaan sebelum menunggu (# 4) mungkin untuk menghemat waktu dari menunggu. Kebebasan yang sama untuk multitask juga tersedia untuk penelepon eksternal (dan semua penelepon dalam rantai) dan ini adalah nilai tambah terbesar dari hal 'async' ini! Anda merasa itu sinkron..atau normal tetapi tidak.

Ingat, metode itu sudah dikembalikan (# 2), tidak bisa kembali lagi (tidak ada kedua kalinya). Jadi bagaimana penelepon tahu? Ini semua tentang Tugas! Tugas telah berlalu. Tugas menunggu (bukan metode, bukan nilai). Nilai akan ditetapkan dalam Tugas. Status tugas akan ditetapkan untuk selesai. Pemanggil hanya memonitor Tugas (# 6). Jadi 6 # adalah jawaban ke mana / siapa yang mendapatkan hasilnya. Bacaan selanjutnya untuk nanti di sini .

Introspeksi pertanyaan demi pembelajaran: 1 menit

Mari kita sesuaikan pertanyaannya sedikit:

Bagaimana dan Kapan menggunakan dan ? asyncawait Tasks

Karena belajar Tasksecara otomatis mencakup dua lainnya (dan menjawab pertanyaan Anda)

Cepat melewati sintaks gula: 5 menit

  • Sebelum konversi (metode asli)

    internal static int Method(int arg0, int arg1) { int result = arg0 + arg1; IO(); // Do some long running IO. return result; }

  • metode Task-ified untuk memanggil metode di atas

    internal static Task<int> MethodTask(int arg0, int arg1) { Task<int> task = new Task<int>(() => Method(arg0, arg1)); task.Start(); // Hot task (started task) should always be returned. return task; }

Apakah kami menyebutkan menunggu atau async? Tidak. Hubungi metode di atas dan Anda mendapatkan tugas yang dapat Anda pantau. Anda sudah tahu tugas apa yang dikembalikan .. integer.

  • Memanggil Tugas sedikit rumit dan saat itulah kata kunci mulai muncul. Mari kita panggil MethodTask ()

    internal static async Task<int> MethodAsync(int arg0, int arg1) { int result = await HelperMethods.MethodTask(arg0, arg1); return result; }

Kode yang sama di atas ditambahkan sebagai gambar di bawah ini: masukkan deskripsi gambar di sini

  1. Kami sedang 'menunggu' tugas untuk diselesaikan. Karena ituawait
  2. Karena kita menggunakan menunggu, kita harus menggunakan async(sintaksis wajib)
  3. MethodAsync dengan Asyncsebagai awalan (standar pengkodean)

awaitmudah dimengerti tetapi dua sisanya ( async, Async) mungkin tidak :). Yah, itu seharusnya lebih masuk akal untuk kompiler sekalipun. Selanjutnya dibaca nanti di sini

Jadi ada 2 bagian.

  1. Buat 'Tugas'
  2. Buat gula sintaksis untuk memanggil tugas ( await+async)

Ingat, kami memiliki penelepon eksternal untuk AccessTheWebAsync () dan penelepon itu juga tidak luput ... artinya perlu await+asyncjuga sama . Dan rantai itu berlanjut. Tetapi akan selalu ada Tasksatu ujung.

Semua baik-baik saja, tetapi satu pengembang terkejut melihat # 1 (Tugas) hilang ...

Bagikan kebingungan pengembang: 5 menit

Pengembang telah membuat kesalahan dengan tidak mengimplementasikan Tasktetapi masih berfungsi! Cobalah untuk memahami pertanyaan dan hanya jawaban yang diterima yang disediakan di sini . Semoga Anda telah membaca dan sepenuhnya dipahami. Ringkasannya adalah bahwa kita mungkin tidak melihat / mengimplementasikan 'Tugas' tetapi dilaksanakan di suatu tempat di kelas induk. Demikian juga dalam contoh kita memanggil yang sudah dibangun MethodAsync()jauh lebih mudah daripada menerapkan metode itu dengan Task( MethodTask()) diri kita sendiri. Sebagian besar pengembang merasa kesulitan untuk Tasksmengubah kode sambil mengonversi kode ke kode Asynchronous.

Kiat: Cobalah untuk menemukan implementasi Async yang ada (seperti MethodAsyncatau ToListAsync) untuk melakukan outsourcing kesulitan. Jadi kita hanya perlu berurusan dengan Async dan menunggu (yang mudah dan sangat mirip dengan kode normal)

Masalah: Cepat ubah implementasi kode normal dunia nyata ke operasi Async: 2 menit

Baris kode yang ditunjukkan di bawah ini di Lapisan Data mulai rusak (banyak tempat). Karena kami memperbarui beberapa kode kami dari .Net framework 4.2. * Ke .Net core. Kami harus memperbaikinya dalam 1 jam di seluruh aplikasi!

var myContract = query.Where(c => c.ContractID == _contractID).First();

mudah!

  1. Kami menginstal paket nuget EntityFramework karena memiliki QueryableExtensions. Atau dengan kata lain itu melakukan implementasi Async (tugas), sehingga kita bisa bertahan dengan kode sederhana Asyncdan awaitdalam.
  2. namespace = Microsoft.EntityFrameworkCore

saluran kode panggilan diubah seperti ini

var myContract = await query.Where(c => c.ContractID == _contractID).FirstAsync();
  1. Metode tanda tangan berubah dari

    Contract GetContract(int contractnumber)

    untuk

    async Task<Contract> GetContractAsync(int contractnumber)

  2. Metode panggilan juga terpengaruh: GetContractAsync(123456);dipanggil sebagaiGetContractAsync(123456).Result;

  3. Kami mengubahnya di mana-mana dalam 30 menit!

Tetapi arsitek mengatakan kepada kami untuk tidak menggunakan perpustakaan EntityFramework hanya untuk ini! Ups! drama! Kemudian kami membuat implementasi Tugas kustom (yuk). Yang kamu tahu caranya. Masih mudah! .. masih yuk ..

Kemana Selanjutnya? Ada video cepat yang luar biasa yang bisa kita tonton tentang Konversi Panggilan Sinkron ke Asinkron di ASP.Net Core , mungkin itu kemungkinan arah yang akan dituju setelah membaca ini.

Awan Biru
sumber
jawaban yang fantastis! ini membantu saya satu ton
cklimowski
1
Jawaban bagus. Anda mungkin ingin memperbaiki beberapa hal kecil seperti: (a) menyebutkan ".Net framework 4.2" (tidak ada versi yang saya ketahui, ada) (b) casing di EntityFrameWork => EntityFramework
immitev
15

Semua jawaban di sini menggunakan Task.Delay()atau asyncfungsi bawaan lainnya. Tapi di sini adalah contoh saya yang tidak menggunakan asyncfungsi - fungsi tersebut:

// Starts counting to a large number and then immediately displays message "I'm counting...". 
// Then it waits for task to finish and displays "finished, press any key".
static void asyncTest ()
{
    Console.WriteLine("Started asyncTest()");
    Task<long> task = asyncTest_count();
    Console.WriteLine("Started counting, please wait...");
    task.Wait(); // if you comment this line you will see that message "Finished counting" will be displayed before we actually finished counting.
    //Console.WriteLine("Finished counting to " + task.Result.ToString()); // using task.Result seems to also call task.Wait().
    Console.WriteLine("Finished counting.");
    Console.WriteLine("Press any key to exit program.");
    Console.ReadLine();
}

static async Task<long> asyncTest_count()
{
    long k = 0;
    Console.WriteLine("Started asyncTest_count()");
    await Task.Run(() =>
    {
        long countTo = 100000000;
        int prevPercentDone = -1;
        for (long i = 0; i <= countTo; i++)
        {
            int percentDone = (int)(100 * (i / (double)countTo));
            if (percentDone != prevPercentDone)
            {
                prevPercentDone = percentDone;
                Console.Write(percentDone.ToString() + "% ");
            }

            k = i;
        }
    });
    Console.WriteLine("");
    Console.WriteLine("Finished asyncTest_count()");
    return k;
}
Nada Soda
sumber
2
Terima kasih! jawaban pertama yang benar-benar berfungsi bukannya menunggu.
Jeffnl
terima kasih telah menunjukkan task.Wait();dan bagaimana ini dapat digunakan untuk menghindari async / menunggu neraka: P
encoder
12

Jawaban ini bertujuan untuk memberikan beberapa info khusus untuk ASP.NET.

Dengan memanfaatkan async / menunggu di pengontrol MVC, dimungkinkan untuk meningkatkan pemanfaatan kumpulan benang dan mencapai throughput yang jauh lebih baik, seperti yang dijelaskan dalam artikel di bawah ini,

http://www.asp.net/mvc/tutorials/mvc-4/using-asynchronous-methods-in-aspnet-mvc-4

Dalam aplikasi web yang melihat sejumlah besar permintaan bersamaan pada saat start-up atau memiliki beban bursty (di mana konkurensi meningkat tiba-tiba), membuat panggilan layanan web ini tidak sinkron akan meningkatkan responsifitas aplikasi Anda. Permintaan asinkron membutuhkan waktu yang sama untuk diproses sebagai permintaan sinkron. Misalnya, jika permintaan membuat panggilan layanan web yang membutuhkan dua detik untuk menyelesaikannya, permintaan tersebut membutuhkan dua detik apakah itu dilakukan secara sinkron atau asinkron. Namun, selama panggilan tidak sinkron, utas tidak diblokir untuk merespons permintaan lain saat menunggu permintaan pertama selesai. Oleh karena itu, permintaan asinkron mencegah permintaan antrian dan pertumbuhan kumpulan utas ketika ada banyak permintaan bersamaan yang menjalankan operasi yang sudah berjalan lama.

Lex Li
sumber
12

Async & Menunggu Penjelasan Sederhana

Analogi Sederhana

Seseorang mungkin menunggu kereta pagi mereka. Ini semua mereka lakukan karena ini adalah tugas utama mereka yang sedang mereka lakukan. (pemrograman sinkron (apa yang biasanya Anda lakukan!))

Orang lain mungkin menunggu kereta pagi mereka sementara mereka merokok dan minum kopi. (Pemrograman asinkron)

Apa itu pemrograman asinkron?

Pemrograman asinkron adalah di mana seorang programmer akan memilih untuk menjalankan beberapa kodenya pada utas terpisah dari utas utama eksekusi dan kemudian memberi tahu utas utama pada penyelesaiannya.

Apa yang sebenarnya dilakukan kata kunci async?

Awalan kata kunci async ke nama metode seperti

async void DoSomething(){ . . .

memungkinkan pemrogram untuk menggunakan kata kunci yang menunggu saat memanggil tugas yang tidak sinkron. Hanya itu yang dilakukannya.

Mengapa ini penting?

Dalam banyak sistem perangkat lunak utas utama dicadangkan untuk operasi yang secara khusus berkaitan dengan Antarmuka Pengguna. Jika saya menjalankan algoritme rekursif yang sangat kompleks yang membutuhkan waktu 5 detik untuk menyelesaikannya di komputer saya, tetapi saya menjalankan ini pada Utas Utama (utas UI) Saat pengguna mencoba mengklik apa pun pada aplikasi saya, itu akan tampak membeku sebagai utas utama saya telah mengantri dan saat ini memproses terlalu banyak operasi. Akibatnya utas utama tidak dapat memproses klik mouse untuk menjalankan metode dari klik tombol.

Kapan Anda menggunakan Async dan Await?

Gunakan kata kunci asinkron secara ideal ketika Anda melakukan sesuatu yang tidak melibatkan antarmuka pengguna.

Jadi katakanlah Anda sedang menulis sebuah program yang memungkinkan pengguna membuat sketsa di ponsel mereka, tetapi setiap 5 detik akan memeriksa cuaca di internet.

Kita harus menunggu panggilan panggilan polling setiap 5 detik ke jaringan untuk mendapatkan cuaca karena pengguna aplikasi harus terus berinteraksi dengan layar sentuh seluler untuk menggambar gambar-gambar cantik.

Bagaimana Anda menggunakan Async dan Menunggu

Berikut dari contoh di atas, berikut adalah beberapa kode pseudo cara menulisnya:

    //ASYNCHRONOUS
    //this is called using the await keyword every 5 seconds from a polling timer or something.

    async Task CheckWeather()
    {
        var weather = await GetWeather();
        //do something with the weather now you have it
    }

    async Task<WeatherResult> GetWeather()
    {

        var weatherJson = await CallToNetworkAddressToGetWeather();
        return deserializeJson<weatherJson>(weatherJson);
    }

    //SYNCHRONOUS
    //This method is called whenever the screen is pressed
    void ScreenPressed()
    {
        DrawSketchOnScreen();
    }

Catatan Tambahan - Perbarui

Saya lupa menyebutkan dalam catatan asli saya bahwa dalam C # Anda hanya bisa menunggu metode yang dibungkus dalam Tugas. misalnya Anda dapat menunggu metode ini:

// awaiting this will return a string.
// calling this without await (synchronously) will result in a Task<string> object.
async Task<string> FetchHelloWorld() {..

Anda tidak dapat menunggu metode yang bukan tugas seperti ini:

async string FetchHelloWorld() {..

Jangan ragu untuk meninjau kode sumber untuk kelas Tugas di sini .

James Mallon
sumber
4
Terima kasih telah meluangkan waktu untuk menulis yang ini.
Prashant
10

Async / Menunggu

Sebenarnya Async / Menunggu adalah sepasang kata kunci yang hanya gula sintaksis untuk membuat panggilan balik dari tugas asinkron.

Ambil contoh operasi ini:

    public static void DoSomeWork()
    {
        var task = Task.Run(() =>
        {
            // [RUNS ON WORKER THREAD]

            // IS NOT bubbling up due to the different threads
            throw new Exception();
            Thread.Sleep(2000);

            return "Hello";
        });

        // This is the callback
        task.ContinueWith((t) => {
            // -> Exception is swallowed silently
            Console.WriteLine("Completed");

            // [RUNS ON WORKER THREAD]
        });
    }

Kode di atas memiliki beberapa kelemahan. Kesalahan tidak diteruskan dan sulit dibaca. Tetapi Async dan Await datang untuk membantu kami:

    public async static void DoSomeWork()
    {
        var result = await Task.Run(() =>
        {
            // [RUNS ON WORKER THREAD]

            // IS bubbling up
            throw new Exception();
            Thread.Sleep(2000);

            return "Hello";
        });

        // every thing below is a callback 
        // (including the calling methods)

        Console.WriteLine("Completed");

    }

Menunggu panggilan harus menggunakan metode Async. Ini memiliki beberapa keunggulan:

  • Mengembalikan hasil Tugas
  • membuat panggilan balik secara otomatis
  • memeriksa kesalahan dan membiarkannya muncul di callstack (hanya sampai tidak ada panggilan yang menunggu di callstack)
  • menunggu hasilnya
  • membebaskan utas utama
  • menjalankan panggilan balik pada utas utama
  • menggunakan thread pekerja dari threadpool untuk tugas tersebut
  • membuat kode mudah dibaca
  • dan masih banyak lagi

CATATAN : Async dan Await digunakan dengan panggilan asinkron untuk tidak melakukan ini. Anda harus menggunakan Libary Tugas untuk ini, seperti Task.Run ().

Berikut ini adalah perbandingan antara menunggu dan tidak menunggu solusi

Ini bukan solusi async:

    public static long DoTask()
    {
        stopWatch.Reset();
        stopWatch.Start();

        // [RUNS ON MAIN THREAD]
        var task = Task.Run(() => {
            Thread.Sleep(2000);
            // [RUNS ON WORKER THREAD]
        });
        // goes directly further
        // WITHOUT waiting until the task is finished

        // [RUNS ON MAIN THREAD]

        stopWatch.Stop();
        // 50 milliseconds
        return stopWatch.ElapsedMilliseconds;
    }

Ini adalah metode async:

    public async static Task<long> DoAwaitTask()
    {
        stopWatch.Reset();
        stopWatch.Start();

        // [RUNS ON MAIN THREAD]

        await Task.Run(() => {
            Thread.Sleep(2000);
            // [RUNS ON WORKER THREAD]
        });
        // Waits until task is finished

        // [RUNS ON MAIN THREAD]

        stopWatch.Stop();
        // 2050 milliseconds
        return stopWatch.ElapsedMilliseconds;
    }

Anda sebenarnya dapat memanggil metode async tanpa menunggu kata kunci tetapi ini berarti bahwa Pengecualian apa pun di sini tertelan dalam mode rilis:

    public static Stopwatch stopWatch { get; } = new Stopwatch();

    static void Main(string[] args)
    {
        Console.WriteLine("DoAwaitTask: " + DoAwaitTask().Result + " ms");
        // 2050 (2000 more because of the await)
        Console.WriteLine("DoTask: " + DoTask() + " ms");
        // 50
        Console.ReadKey();
    }

Async dan Await tidak dimaksudkan untuk komputasi paralel. Mereka digunakan untuk tidak memblokir utas utama Anda. Ketika itu tentang asp.net atau aplikasi Windows, memblokir utas Anda karena panggilan jaringan adalah hal yang buruk. Jika Anda melakukan ini, aplikasi Anda akan menjadi tidak responsif atau bahkan macet.

Lihat ms docs untuk lebih banyak contoh.

Hakim
sumber
9

Sejujurnya saya masih berpikir penjelasan terbaik adalah tentang masa depan dan janji di Wikipedia: http://en.wikipedia.org/wiki/Futures_and_promises

Ide dasarnya adalah bahwa Anda memiliki kumpulan utas terpisah yang menjalankan tugas secara tidak sinkron. Saat menggunakannya. Namun objek tersebut membuat janji bahwa ia akan menjalankan operasi pada suatu waktu dan memberikan Anda hasilnya ketika Anda memintanya. Ini berarti bahwa itu akan memblokir ketika Anda meminta hasil dan belum selesai, tetapi jalankan di kolam utas sebaliknya.

Dari sana Anda dapat mengoptimalkan hal-hal: beberapa operasi dapat diimplementasikan async dan Anda dapat mengoptimalkan hal-hal seperti file IO dan komunikasi jaringan dengan mengelompokkan bersama permintaan berikutnya dan / atau menyusun ulang mereka. Saya tidak yakin apakah ini sudah ada dalam kerangka tugas Microsoft - tetapi jika tidak, itu akan menjadi salah satu hal pertama yang akan saya tambahkan.

Anda benar-benar dapat menerapkan pola sort-of masa depan dengan hasil dalam C # 4.0. Jika Anda ingin tahu cara kerjanya tepatnya, saya dapat merekomendasikan tautan ini yang melakukan pekerjaan yang layak: http://code.google.com/p/fracture/source/browse/trunk/Squared/TaskLib/ . Namun, jika Anda mulai mempermainkannya sendiri, Anda akan melihat bahwa Anda benar-benar membutuhkan dukungan bahasa jika Anda ingin melakukan semua hal keren - yang persis seperti yang dilakukan Microsoft.

atlaste
sumber
8

Lihat biola ini https://dotnetfiddle.net/VhZdLU (dan tingkatkan jika memungkinkan) untuk menjalankan aplikasi konsol sederhana yang menunjukkan penggunaan Tugas, Tugas. Tunggu Semua (), asinkron, dan tunggu operator dalam program yang sama.

Biola ini akan menghapus konsep siklus eksekusi Anda.

Ini kode contohnya

using System;
using System.Threading.Tasks;

public class Program
{
    public static void Main()
    {               
        var a = MyMethodAsync(); //Task started for Execution and immediately goes to Line 19 of the code. Cursor will come back as soon as await operator is met       
        Console.WriteLine("Cursor Moved to Next Line Without Waiting for MyMethodAsync() completion");
        Console.WriteLine("Now Waiting for Task to be Finished");       
        Task.WaitAll(a); //Now Waiting      
        Console.WriteLine("Exiting CommandLine");       
    }

    public static async Task MyMethodAsync()
    {
        Task<int> longRunningTask = LongRunningOperation();
        // independent work which doesn't need the result of LongRunningOperationAsync can be done here
        Console.WriteLine("Independent Works of now executes in MyMethodAsync()");
        //and now we call await on the task 
        int result = await longRunningTask;
        //use the result 
        Console.WriteLine("Result of LongRunningOperation() is " + result);
    }

    public static async Task<int> LongRunningOperation() // assume we return an int from this long running operation 
    {
        Console.WriteLine("LongRunningOperation() Started");
        await Task.Delay(2000); // 2 second delay
        Console.WriteLine("LongRunningOperation() Finished after 2 Seconds");
        return 1;
    }   

}

Jejak yang berasal dari Output Window: masukkan deskripsi gambar di sini

vibs2006
sumber
3
public static void Main(string[] args)
{
    string result = DownloadContentAsync().Result;
    Console.ReadKey();
}

// You use the async keyword to mark a method for asynchronous operations.
// The "async" modifier simply starts synchronously the current thread. 
// What it does is enable the method to be split into multiple pieces.
// The boundaries of these pieces are marked with the await keyword.
public static async Task<string> DownloadContentAsync()// By convention, the method name ends with "Async
{
    using (HttpClient client = new HttpClient())
    {
        // When you use the await keyword, the compiler generates the code that checks if the asynchronous operation is finished.
        // If it is already finished, the method continues to run synchronously.
        // If not completed, the state machine will connect a continuation method that must be executed WHEN the Task is completed.


        // Http request example. 
        // (In this example I can set the milliseconds after "sleep=")
        String result = await client.GetStringAsync("http://httpstat.us/200?sleep=1000");

        Console.WriteLine(result);

        // After completing the result response, the state machine will continue to synchronously execute the other processes.


        return result;
    }
}
Weslley Rufino de Lima
sumber
3

Di tingkat yang lebih tinggi:

1) Kata kunci Async memungkinkan menunggu dan hanya itu yang dilakukannya. Kata kunci Async tidak menjalankan metode dalam utas terpisah. Metode f async awal berjalan secara sinkron hingga hits menunggu tugas yang memakan waktu.

2) Anda bisa menunggu pada metode yang mengembalikan tugas atau tugas tipe T. Anda tidak bisa menunggu pada metode batal async.

3) Saat utas utama bertemu menunggu tugas yang memakan waktu atau ketika pekerjaan sebenarnya dimulai, utas utama kembali ke pemanggil metode saat ini.

4) Jika utas utama melihat menunggu pada tugas yang masih dieksekusi, itu tidak menunggu dan kembali ke pemanggil metode saat ini. Dengan cara ini, aplikasi tetap responsif.

5) Menunggu pada tugas pemrosesan, sekarang akan dijalankan pada utas terpisah dari kumpulan utas.

6) Ketika tugas menunggu ini selesai, semua kode di bawah ini akan dieksekusi oleh utas terpisah

Di bawah ini adalah kode sampel. Jalankan dan periksa id utas

using System;
using System.Threading;
using System.Threading.Tasks;

namespace AsyncAwaitDemo
{
    class Program
    {
        public static async void AsynchronousOperation()
        {
            Console.WriteLine("Inside AsynchronousOperation Before AsyncMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
            //Task<int> _task = AsyncMethod();
            int count = await AsyncMethod();

            Console.WriteLine("Inside AsynchronousOperation After AsyncMethod Before Await, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            //int count = await _task;

            Console.WriteLine("Inside AsynchronousOperation After AsyncMethod After Await Before DependentMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            DependentMethod(count);

            Console.WriteLine("Inside AsynchronousOperation After AsyncMethod After Await After DependentMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
        }

        public static async Task<int> AsyncMethod()
        {
            Console.WriteLine("Inside AsyncMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
            int count = 0;

            await Task.Run(() =>
            {
                Console.WriteLine("Executing a long running task which takes 10 seconds to complete, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(20000);
                count = 10;
            });

            Console.WriteLine("Completed AsyncMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            return count;
        }       

        public static void DependentMethod(int count)
        {
            Console.WriteLine("Inside DependentMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId + ". Total count is " + count);
        }

        static void Main(string[] args)
        {
            Console.WriteLine("Started Main method, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            AsynchronousOperation();

            Console.WriteLine("Completed Main method, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            Console.ReadKey();
        }

    }
}
ABajpai
sumber
2

Cara saya mengerti juga, harus ada jabatan ketiga ditambahkan ke dalam campuran: Task.

Async hanyalah kualifikasi yang Anda masukkan pada metode Anda untuk mengatakan itu adalah metode asinkron.

Taskadalah kembalinya asyncfungsi. Ini dijalankan secara tidak sinkron.

Anda awaitseorang Tugas. Ketika eksekusi kode mencapai garis ini, kontrol melompat kembali ke pemanggil fungsi asli Anda di sekitarnya.

Jika sebaliknya, Anda menetapkan kembalinya sebuah asyncfungsi (yaitu Task) ke variabel, ketika eksekusi kode mencapai garis ini, itu hanya terus melewati garis yang dalam fungsi sekitarnya sementara yang Taskmengeksekusi asynchronous.

pengguna21306
sumber
1

apakah menggunakannya sama dengan pemunculan utas latar untuk melakukan logika durasi panjang?

Artikel ini MDSN: Pemrograman Asinkron dengan async dan menunggu (C #) menjelaskannya secara eksplisit:

Async dan menunggu kata kunci tidak menyebabkan utas tambahan dibuat. Metode Async tidak memerlukan multithreading karena metode async tidak berjalan pada utasnya sendiri. Metode ini berjalan pada konteks sinkronisasi saat ini dan menggunakan waktu pada utas hanya ketika metode ini aktif.

Dmitry G.
sumber
1

Dalam kode berikut ini, metode HttpClient GetByteArrayAsync mengembalikan Tugas, getContentsTask. Tugas adalah janji untuk menghasilkan array byte yang sebenarnya ketika tugas selesai. Operator yang menunggu diterapkan untuk getContentsTask untuk menunda eksekusi di SumPageSizesAsync sampai getContentsTask selesai. Sementara itu, kontrol dikembalikan ke penelepon SumPageSizesAsync. Ketika getContentsTask selesai, ekspresi menunggu mengevaluasi ke array byte.

private async Task SumPageSizesAsync()
{
    // To use the HttpClient type in desktop apps, you must include a using directive and add a 
    // reference for the System.Net.Http namespace.
    HttpClient client = new HttpClient();
    // . . .
    Task<byte[]> getContentsTask = client.GetByteArrayAsync(url);
    byte[] urlContents = await getContentsTask;

    // Equivalently, now that you see how it works, you can write the same thing in a single line.
    //byte[] urlContents = await client.GetByteArrayAsync(url);
    // . . .
}
lazydeveloper
sumber
1

Di bawah ini adalah kode yang membaca file excel dengan membuka dialog dan kemudian menggunakan async dan menunggu untuk menjalankan asinkron kode yang membaca satu per satu baris dari excel dan mengikat ke grid

namespace EmailBillingRates
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            lblProcessing.Text = "";
        }

        private async void btnReadExcel_Click(object sender, EventArgs e)
        {
            string filename = OpenFileDialog();

            Microsoft.Office.Interop.Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application();
            Microsoft.Office.Interop.Excel.Workbook xlWorkbook = xlApp.Workbooks.Open(filename);
            Microsoft.Office.Interop.Excel._Worksheet xlWorksheet = xlWorkbook.Sheets[1];
            Microsoft.Office.Interop.Excel.Range xlRange = xlWorksheet.UsedRange;
            try
            {
                Task<int> longRunningTask = BindGrid(xlRange);
                int result = await longRunningTask;

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message.ToString());
            }
            finally
            {
                //cleanup  
               // GC.Collect();
                //GC.WaitForPendingFinalizers();

                //rule of thumb for releasing com objects:  
                //  never use two dots, all COM objects must be referenced and released individually  
                //  ex: [somthing].[something].[something] is bad  

                //release com objects to fully kill excel process from running in the background  
                Marshal.ReleaseComObject(xlRange);
                Marshal.ReleaseComObject(xlWorksheet);

                //close and release  
                xlWorkbook.Close();
                Marshal.ReleaseComObject(xlWorkbook);

                //quit and release  
                xlApp.Quit();
                Marshal.ReleaseComObject(xlApp);
            }

        }

        private void btnSendEmail_Click(object sender, EventArgs e)
        {

        }

        private string OpenFileDialog()
        {
            string filename = "";
            OpenFileDialog fdlg = new OpenFileDialog();
            fdlg.Title = "Excel File Dialog";
            fdlg.InitialDirectory = @"c:\";
            fdlg.Filter = "All files (*.*)|*.*|All files (*.*)|*.*";
            fdlg.FilterIndex = 2;
            fdlg.RestoreDirectory = true;
            if (fdlg.ShowDialog() == DialogResult.OK)
            {
                filename = fdlg.FileName;
            }
            return filename;
        }

        private async Task<int> BindGrid(Microsoft.Office.Interop.Excel.Range xlRange)
        {
            lblProcessing.Text = "Processing File.. Please wait";
            int rowCount = xlRange.Rows.Count;
            int colCount = xlRange.Columns.Count;

            // dt.Column = colCount;  
            dataGridView1.ColumnCount = colCount;
            dataGridView1.RowCount = rowCount;

            for (int i = 1; i <= rowCount; i++)
            {
                for (int j = 1; j <= colCount; j++)
                {
                    //write the value to the Grid  
                    if (xlRange.Cells[i, j] != null && xlRange.Cells[i, j].Value2 != null)
                    {
                         await Task.Delay(1);
                         dataGridView1.Rows[i - 1].Cells[j - 1].Value =  xlRange.Cells[i, j].Value2.ToString();
                    }

                }
            }
            lblProcessing.Text = "";
            return 0;
        }
    }

    internal class async
    {
    }
}
Zaheer Abbas
sumber
0

Jawaban di sini bermanfaat sebagai pedoman umum tentang await / async. Mereka juga berisi beberapa detail tentang bagaimana kabel menunggu / async. Saya ingin berbagi pengalaman praktis dengan Anda yang harus Anda ketahui sebelum menggunakan pola desain ini.

Istilah "menunggu" adalah literal, jadi utas apa pun yang Anda panggil akan menunggu hasil metode sebelum melanjutkan. Di latar depan , ini adalah bencana . Utas latar depan memikul beban untuk membangun aplikasi Anda, termasuk tampilan, model tampilan, animasi awal, dan apa pun yang Anda miliki. Jadi ketika Anda menunggu utas latar depan, Anda menghentikan aplikasi. Pengguna menunggu dan menunggu ketika tidak ada yang tampak terjadi. Ini memberikan pengalaman pengguna yang negatif.

Anda tentu bisa menunggu utas latar menggunakan berbagai cara:

Device.BeginInvokeOnMainThread(async () => { await AnyAwaitableMethod(); });

// Notice that we do not await the following call, 
// as that would tie it to the foreground thread.
try
{
Task.Run(async () => { await AnyAwaitableMethod(); });
}
catch
{}

Kode lengkap untuk komentar ini ada di https://github.com/marcusts/xamarin-forms-annoyances . Lihat solusi yang disebut AwaitAsyncAntipattern.sln.

Situs GitHub juga menyediakan tautan ke diskusi yang lebih rinci tentang topik ini.

Stephen Marcus
sumber
1
Dari apa yang saya mengerti, async / awaitadalah sintaksis gula untuk callback, itu tidak ada hubungannya dengan threading. msdn.microsoft.com/en-us/magazine/hh456401.aspx Ini untuk kode terikat non-CPU, misalnya menunggu input atau penundaan. Task.Runseharusnya hanya digunakan untuk kode yang terikat CPU blog.stephencleary.com/2013/10/…
geometrikal
The term "await" is literal, so whatever thread you call it on will wait for the result of the method before continuing.Ini tidak benar - mungkin maksud Anda Task.Wait ()? Ketika Anda menggunakan await, itu menetapkan sisa metode sebagai kelanjutan yang akan dieksekusi ketika apa pun yang Anda tunggu-ed selesai. Itu keluar dari metode yang Anda gunakan, sehingga penelepon dapat melanjutkan. Kemudian ketika baris yang ditunggu-tunggu benar-benar selesai, ia menyelesaikan sisa metode itu pada beberapa utas (biasanya utas pekerja).
Don Cheadle
@geometrikal pada intinya, async/awaitadalah tentang membebaskan .NET Threads. Saat Anda awaitmenjalankan operasi yang benar-benar-async (seperti File.WriteAsync .NET), itu menangguhkan sisa metode yang Anda gunakan await, sehingga penelepon dapat melanjutkan dan berpotensi menyelesaikan tujuannya. Tidak ada pemblokiran thread atau menunggu awaitoperasi -ed. Ketika operasi yang Anda awaitselesaikan selesai, sisa async/awaitmetode ini diletakkan di utas dan dieksekusi (mirip dengan ide panggilan balik).
Don Cheadle