异步运行同步方法的正确方法是什么

时间:2017-01-18 09:51:30

标签: c# asynchronous task-parallel-library .net-4.5

我有一个抽象类,其方法定义如下

public abstract class FooBase : IDisposable
{
    protected abstract bool GetData(params object[] getDataParams);
}

我正在使用这样的抽象类并尝试异步运行GetData方法

public class SomeClass : FooBase
{
    protected override bool GetData(params object[] getDataParams)
    {
        var task = Task.Factory.StartNew(() =>
        {
            using (var adapter = new DataAdapter())
            {
                // Take your time and fetch the data
            }
        });
        task.Wait();
        return true;
    }
}

我想知道的是:我是做对还是错,或者是否还有其他更好的方法来达到同一目标。

更新

如果我改变我的方法,那么它将被异步调用

public class SomeClass : FooBase
{
    protected override bool GetData(params object[] getDataParams)
    {
        var task = Task.Factory.StartNew(async () =>
        {
            using (var adapter = new DataAdapter())
            {
                // Take your time and fetch the data
            }
        });
        task.Wait();
        return true;
    }
}

感谢您对此的评论。

1 个答案:

答案 0 :(得分:3)

除了同步之外,无法运行同步方法。

你可以将结果包装成看起来像异步运行的东西(例如。Task.FromResult),或者在另一个线程中运行。 1 但是同步方法仍会阻塞线程它正在运行。

(相反,阻塞异步操作,很简单。这就是为什么你需要让底层操作异步,因为你可以在顶层构建同步和异步方法。)

更新(有关更新):

附加代码 - 特别是task.Wait()语句 - 将导致调用者的线程在等待任务完成时阻塞。该任务将在另一个线程上运行,导致该线程被阻塞。 IE浏览器。你导致两个线程(调用者和一个线程池线程)阻塞。如果直接调用底层方法,则只会阻塞调用方的线程。

您有两种方法:

  1. 最佳:使用ADO.NET的异步操作。 (这意味着不使用DataTables / DataAdaptor,但恕我直言,这是一个很好的举动:他们所做的就是将应该在数据库上进行的操作移到客户端。)

    < / LI>
  2. 卸载到另一个线程,但向调用者返回Task<TResult>,仅将Task标记为完成,然后基础操作完成。类似的东西:

    protected override Task<bool> GetData(params object[] getDataParams) {
      var tcs = new TaskCompletionSource<bool>();
      Task.Factory.StartNew(async () => {
        using (var adapter = new DataAdapter()) {
          // Take your time and fetch the data
          tcs.SetResult(the-result);
        }
      });
      return tcs.Task;
    }
    

    请注意,GetData的返回值为Task<bool>:调用者需要等待或同时执行其他操作,然后获取数据操作的结果。如果调用者只是等待,那么你有两个被阻塞的线程。 (C#5&#39; s await不同:调用者也变得异步。)

  3. 1 为避免疑问:这可以看起来像一个正常的,Task<T>返回的异步方法(例如,利用TaskCompletionSource<T>卸载从当前线程阻塞到线程池中的某个线程)。但它仍然是一个阻塞操作:只是阻塞一个不同的线程。