Arabirim uygulamalarını eşzamansız yapma


116

Şu anda bazı Async yöntemlerini kullanarak uygulamamı yapmaya çalışıyorum. Tüm IO'm, bir arayüzün açık uygulamaları aracılığıyla yapılıyor ve işlemleri eşzamansız hale getirme konusunda biraz kafam karıştı.

Gördüğüm kadarıyla uygulamada iki seçeneğim var:

interface IIO
{
    void DoOperation();
}

SEÇENEK1: Zaman uyumsuz örtük bir uygulama yapın ve örtük uygulamada sonucu bekleyin.

class IOImplementation : IIO
{

     async void DoOperation()
    {
        await Task.Factory.StartNew(() =>
            {
                //WRITING A FILE OR SOME SUCH THINGAMAGIG
            });
    }

    #region IIO Members

    void IIO.DoOperation()
    {
        DoOperation();
    }

    #endregion
}

SEÇENEK2: Açık uygulamayı zaman uyumsuz yapın ve görevi örtük uygulamadan bekleyin.

class IOAsyncImplementation : IIO
{
    private Task DoOperationAsync()
    {
        return new Task(() =>
            {
                //DO ALL THE HEAVY LIFTING!!!
            });
    }

    #region IIOAsync Members

    async void IIO.DoOperation()
    {
        await DoOperationAsync();
    }

    #endregion
}

Bu uygulamalardan biri diğerinden daha mı iyi yoksa benim düşünmediğim başka bir yol var mı?

Yanıtlar:


231

Bu seçeneklerin hiçbiri doğru değil. Zaman uyumsuz bir arayüz uygulamaya çalışıyorsunuz. Bunu yapma. Sorun şu ki, DoOperation()geri döndüğünde işlem henüz tamamlanmayacak. Daha da kötüsü, işlem sırasında bir istisna olursa (ki bu, IO işlemlerinde çok yaygındır), kullanıcının bu istisnayla başa çıkma şansı olmayacaktır.

Yapmanız gereken , arabirimi eşzamansız olacak şekilde değiştirmektir :

interface IIO
{
    Task DoOperationAsync(); // note: no async here
}

class IOImplementation : IIO
{
    public async Task DoOperationAsync()
    {
        // perform the operation here
    }
}

Bu şekilde kullanıcı işlemin olduğunu asyncgörecek ve yapabilecektir await. Bu aynı zamanda kodunuzun kullanıcılarını geçiş yapmaya zorlar async, ancak bu kaçınılmazdır.

Ayrıca, StartNew()uygulamanızda kullanmanın sadece bir örnek olduğunu varsayıyorum , asenkron IO uygulamak için buna ihtiyacınız olmamalı. (Ve new Task()değil mi, çünkü o bile işleyecek, daha da kötü .)Start()Task


Bu, açık bir uygulamayla nasıl görünür? Ayrıca, bu uygulamayı nerede bekliyorsunuz?
Moriya

1
@Animal Açık uygulanması her zaman (sadece eklerken, aynı görünecektir async): async Task IIO.DoOperationAsync(). Ve nereye awaitgeri dönüyorsun demek istiyorsun Task? Nereyi ararsanız arayın DoOperationAsync().
svick

Temel olarak, sorumu "Nerede bekliyorum?" Şeklinde özetleyebileceğimi düşünüyorum. Zaman uyumsuz yöntemin içinde beklemiyorsam derleme uyarıları alıyorum.
Moriya

1
İdeal olarak, IO kodunu sarmalamanız gerekmez Task.Run(), bu IO kodunun kendisi asenkron olmalıdır ve bunu awaitdoğrudan yaparsınız . Örn line = await streamReader.ReadLineAsync().
svick

4
O halde kodunuzu eşzamansız yapmanın pek bir anlamı yoktur. Makale bakın Meli ben senkron yöntemleri için asenkron sarmalayıcılarını maruz?
svick

19

Daha iyi çözüm, zaman uyumsuz işlemler için başka bir arabirim sunmaktır. Yeni arayüz, orijinal arayüzden miras almalıdır.

Misal:

interface IIO
{
    void DoOperation();
}

interface IIOAsync : IIO
{
    Task DoOperationAsync();
}


class ClsAsync : IIOAsync
{
    public void DoOperation()
    {
        DoOperationAsync().GetAwaiter().GetResult();
    }

    public async Task DoOperationAsync()
    {
        //just an async code demo
        await Task.Delay(1000);
    }
}


class Program
{
    static void Main(string[] args)
    {
        IIOAsync asAsync = new ClsAsync();
        IIO asSync = asAsync;

        Console.WriteLine(DateTime.Now.Second);

        asAsync.DoOperation();
        Console.WriteLine("After call to sync func using Async iface: {0}", 
            DateTime.Now.Second);

        asAsync.DoOperationAsync().GetAwaiter().GetResult();
        Console.WriteLine("After call to async func using Async iface: {0}", 
            DateTime.Now.Second);

        asSync.DoOperation();
        Console.WriteLine("After call to sync func using Sync iface: {0}", 
            DateTime.Now.Second);

        Console.ReadKey(true);
    }
}

PS Eşzamansız işlemlerinizi, gerçekten void döndürmeniz gerekmedikçe, void yerine Task döndürecek şekilde yeniden tasarlayın.


5
Neden GetAwaiter().GetResult()yerine değil Wait()? Bu şekilde AggregateException, iç istisnayı almak için bir paketi açmanıza gerek kalmaz .
Tagc

Bir varyasyon (muhtemelen açık) arabirimleri sınıf uygulayan birden itimat geçerli: class Impl : IIO, IIOAsync. Bununla birlikte, IIO ve IIOAsync'in kendileri, 'eski sözleşmeleri' daha yeni koda çekmekten kaçınabilecek farklı sözleşmelerdir. var c = new Impl(); IIOAsync asAsync = c; IIO asSync = c.
user2864740
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.