9 Temmuz 2018 Pazartesi

async ve await

Giriş
await, async ile beraber ya da tek başına kullanılabilir.

await Tek Başına
await asenkron bir işin bitmesini bekle olarak düşünülebilir. await olmadan asenkron işin Result alanına erşimek doğru değil.

Örnek
İkisinin farkını MSDN şöyle bir örnekle gösteriyor.
async Task<int> AccessTheWebAsync()
{ 
  HttpClient client = new HttpClient();
  Task<string> getStringTask = client.GetStringAsync("http://...");

  DoIndependentWork();

  string urlContents = await getStringTask;

  return urlContents.Length;
}
Tek başına kullanılmasının açıklaması şöyle
If AccessTheWebAsync doesn't have any work that it can do between calling GetStringAsync and awaiting its completion, you can simplify your code by calling and awaiting in the following single statement.
Eğer arada DoIndenpendentWork() metoduna ihtiyaç yoksa sadece şöyle yaparız.
string urlContents = await client.GetStringAsync();
Örnek
pull based ve push based iki işin farkı gibi de düşünülebilir. pull based tost için şöyle yaparız.
// I want some toast.  I will pull it out of the toaster when it is done.
// I will do nothing until the toast is available.
Toast t = toaster.MakeToast();
t.AddJam();
// Yum.
push based tost için şöyle yaparız.
// I want some toast.  But it might take a while and I can do more work
// while I am waiting:
Task<Toast> task = toaster.MakeToastAsync();
Toast t = await task;
// await returns to the caller if the toast is not ready, and assigns
// a callback. When the toast is ready, the callback causes this method
// to start again from this point:
t.AddJam();
// Yum.
Örnek
Elimizde şöyle bir kod olsun.
var user = await _userRepo.GetByUsername(User.Identity.Name);

//Some minor work that doesn't rely on the user object

user = await _userRepo.UpdateLastAccessed(user, DateTime.Now);

return user;
GetByUserName paralel çalışırken ane kod  devam etsin istersek şöyle yaparız.
var userTask = _userRepo.GetByUsername(User.Identity.Name);    
//Some work that doesn't rely on the user object    
user = await _userRepo.UpdateLastAccessed(await userTask, DateTime.Now);    
return user;
userTask'ı await ile beklemek gerekir. Şu kod yanlış çünkü userTask'ın Result alanına eriştiğimizde henüz bitmemiş olabilir.
var userTask = _userRepo.GetByUsername(User.Identity.Name);

//Some work that doesn't rely on the user object

user = await _userRepo.UpdateLastAccessed(userTask.Result, DateTime.Now);

return user;
async Return Type
async metodlar genelde Task, Task<T> veya void dönerler.

async ve await ikilisi
async ve await Continuation yapılacağı zaman kullanılır.  Yani şu async işaretli bir metod içinde bulunan şu kod parçası
var str = await SomeFuncAsync() //returning "test";
this.textBox1.Text = str;
aslında şuna eşittir.
Task.Factory.StartNew(() => { return "test"; } )
            .ContinueWith(task => { this.textBox1.Text = task.Result; },
                            CancellationToken.None,
                            TaskContinuationOptions.None, 
                            TaskScheduler.FromCurrentSynchronizationContext());
async ve StateMachine
async kelimesini gören derleyici
System.Runtime.CompilerServices.AsyncStateMachineAttribute
sınıfını kullanarak bir State Machine oluşturur.

async Event Handler
async Event Handler yazısına taşıdım.

async lambda
async Event Handler yazısına taşıdım.

deadlock
async metodlar eğer yanlış kullanılırsa deadlock'a sebep olabiliyor ancak .Net içindeki sınıflar async/await kullanmadıkları için rahatça kullanılabilir.

async metod içinde await ne işe yarar
await Task'ın bitmesini  beklemek için kullanılır. await ile Task.Factory. Start kullanılabilir.
public async Task FooAsync()
{
    int value = await Task.Factory.StartNew(
                        () => DoSomething(), TaskCreationOptions.LongRunning);
}
veya Task.Run'da kullanılabilir.
public async Task FooAsync()
{
    int value = await Task.Run(() => DoSomething());
}
veya asenkron çalışan bir metod çağrısı yapılır. Şöyle yaparız.
public static async Task <string> job()
{
  ...
  await Task.Delay(2000);
  ...
}
Eğer bunları yapmazsak şöyle bir hata alırız.
Severity Code Description Project File Line Column Warning CS1998 This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. 
Örneğin aşağdaki kod bu hatayı verir.
public async Task<int> GetData() {
        return 1;
}
async metod içinde await ve SyncronizationContext
await çağrılınca altta devam eden kodun kendisini çağıran thread içinde çalışması için SynchronizationContext'i saklar. Şöyle bir kodumuz olsun.
public async Task X()
{
    A();
    await BAsynch();
    C();
}
await çağrısından sonra gelen C() şu hale getirilir.
public Task X()
{
    A();
    return BAsynch().ContinueWith(()=>{ C(); })
}
Dolayısıyla artık Invoke(), BeginInvoke() gibi metodları kullanmaya gerek kalmıyor.

SyncronizationContext Her Thread'de Bulunur mu ?
SynchronizationContext GUI thread'lerinde bulunur ancak konsol thread'lerinde bulunmaz. Dolayısıyla konsolda çalışan bu örnekte farklı sonuçlar görebiliriz.
static  void Main(string[] args)
{
  Console.WriteLine("Main: " + Thread.CurrentThread.ManagedThreadId);//8
  MainAsync(args).Wait();
  Console.WriteLine("Main End: " + Thread.CurrentThread.ManagedThreadId);//8

  Console.ReadKey();
}


static async Task MainAsync(string[] args)
{
  Console.WriteLine("Main Async: " + Thread.CurrentThread.ManagedThreadId);//8

  await thisIsAsync();
}

private static async Task thisIsAsync()
{
  Console.WriteLine("AsyncStart: " + Thread.CurrentThread.ManagedThreadId);//8
  await Task.Delay(1);
  Console.WriteLine("AsyncEnd: " + Thread.CurrentThread.ManagedThreadId);//9

}
async metod içinde await ve Exception
Şöyle yaparız.
async void btncheckPassword_Click(object sender, EventArgs e)
{

  try
  {
    await Task.Run(
      () => 
      {  
        try
        {
          ... //Exception thrown from async method
        }
        catch
        {
          throw new XXX(); //Catch and throw XXX
        }
      }); 

  }
  catch (XXX e) //Code is executed in SynchronizationContext
  {
    ...
  }
}






Hiç yorum yok:

Yorum Gönder