Zdarzenia asynchroniczne umożliwiają wykonanie czasochłonnego zadania w nowym wątku roboczym, który przydzielany jest z puli wątków.
W celu utworzenia zdarzenia asynchronicznego tworzymy delegata i wywołujemy go za pomocą metody BeginInvoke. Argumentami metody są: delegat AsyncCallback, który określa metodę wywoływaną zwrotnie po zakończeniu działania asynchronicznego i obiekt, który zostanie do niej przekazany.
Metoda zwrotna jest uruchamiana w odrębnym wątku.
Po zakończeniu działania metody asynchronicznej należy na obiekcie delegata wywołać metodę EndInvoke. Metodę można wywołać po sprawdzeniu zakończenia działania asynchronicznego (metoda BeginInvoke zwraca obiekt IAsyncResult, który zawiera właściwość IsCompleted i WaitHandle) lub w wywołaniu zwrotnym po zakończeniu działania. Metoda EndInvoke powinna być zawsze wywołana, ponieważ pozwala wykryć wyjątki zgłoszone podczas wywołania asynchronicznego, a także informuje o możliwości zwolnienia zużytych zasobów przy tworzeniu wywołania asynchronicznego.
Wywołując metodę EndInvoke poza wywołaniem zwrotnym po zakończeniu działania asynchronicznego należy pamiętać, że na czas jej wywołania i zakończenia wywołania asynchronicznego zostanie zablokowany wątek, z którego została wywołana.
Przykładowe użycie IsCompleted oraz WaitHandle:
delegate int BigJobCallback();
//...
int BigJob()
{
Thread.Sleep(5000); // 5s
return 123;
}
//...
BigJobCallback job = new BigJobCallback(BigJob);
IAsyncResult result = job.BeginInvoke(null, null);
//...
while (!result.IsCompleted)
result.AsyncWaitHandle.WaitOne(10, false); // czekaj 10ms
int r = job.EndInvoke(result);
W przypadku uruchamiania kilku asynchronicznych metod możemy zaczekać na ich zakończenie korzystając z metody statycznej WaitAll z klasy WaitHandle przekazując do niej tablicę obiektów WaitHandle.
delegate int BigJobCallback(int job);
static int BigJob(int job)
{
Console.WriteLine("Job {0} started...", job);
int[] delays = new int[] { 1000, 2000, 500 };
Random rand = new Random();
int delay = rand.Next(delays.Length);
Thread.Sleep(delays[delay]); // 10s
return job;
}
//...
// trzy "duże" zadania
BigJobCallback[] jobs = new BigJobCallback[] { BigJob, BigJob, BigJob };
// rezultaty wywołań aynchronicznych
IAsyncResult[] results = new IAsyncResult[jobs.Length];
// uchwyty do obsługi oczekiwania na zakończenie wywołań asynchronicznych
WaitHandle[] handles = new WaitHandle[jobs.Length];
for (int index = 0; index < jobs.Length; index++)
{
BigJobCallback job = jobs[index];
results[index] = job.BeginInvoke(index + 1, null, null);
handles[index] = results[index].AsyncWaitHandle;
}
WaitHandle.WaitAll(handles);
for (int index = 0; index < jobs.Length; index++)
{
int job = jobs[index].EndInvoke(results[index]);
Console.WriteLine("Job {0} completed", job);
}
Kompletny przykład zastosowania wywołań asynchronicznych wraz z wywołaniami na ich zakończenie:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace kq.Research.Basics
{
class Example
{
public delegate void Callback();
public event Callback CallbackEvent;
public void Run()
{
if (CallbackEvent != null)
{
foreach (Callback callback in CallbackEvent.GetInvocationList())
{
//direct call:
//callback();
//async call:
callback.BeginInvoke(new AsyncCallback(Complete), callback);
}
}
}
public void Complete(IAsyncResult async)
{
Console.WriteLine("Job {0}.", async.IsCompleted ? "completed" : "failed");
Callback callback = (Callback)async.AsyncState;
callback.EndInvoke(async);
}
}
class Usage
{
public Usage(string name)
{
Name = name;
}
public string Name { get; set; }
public void Subscribe(Example e)
{
if (e != null)
e.CallbackEvent += new Example.Callback(Work);
}
void Work()
{
Console.WriteLine("Begin {0} big job...", Name);
Thread.Sleep(5000);
Console.WriteLine("Complete {0} big job.", Name);
}
}
class Program
{
static void Main(string[] args)
{
Example example = new Example();
Usage usage1 = new Usage("first");
usage1.Subscribe(example);
Usage usage2 = new Usage("second");
usage2.Subscribe(example);
example.Run();
Console.ReadLine();
}
}
}
0 Komentarzy