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

C# 异步运行同步方法的正确方法是什么,c#,asynchronous,task-parallel-library,.net-4.5,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[] g

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

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但同步方法仍将阻止它正在运行的线程

(相反,异步操作上的阻塞非常简单。这就是为什么您需要使底层操作异步,因为您可以在上面构建同步和异步方法。)

更新(有关更新):

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

您有两种方法:

  • 最佳:使用ADO.NET的异步操作。(这意味着不使用
    数据表
    /
    数据适配器
    ,但无论如何,这是一个好的移动:它们所做的只是将应该在数据库上执行的操作移动到客户端。)

  • 卸载到另一个线程,但将
    任务
    返回给调用者,仅将
    任务
    标记为已完成,则基础操作已完成。比如:

    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(参数对象[]getDataParams){
    var tcs=new TaskCompletionSource();
    Task.Factory.StartNew(异步()=>{
    使用(var adapter=new DataAdapter()){
    //慢慢来取数据
    tcs.SetResult(结果);
    }
    });
    返回tcs.Task;
    }
    
    注意这里从
    GetData
    返回是一个
    Task
    :调用者需要等待或同时执行其他操作,然后获取数据操作的结果。如果调用方只是等待,那么您有两个阻塞的线程。(C#5的
    await
    不同:调用者也变得异步。)



  • 1为免生疑问:可以使其看起来像一个正常的,
    任务
    返回的异步方法(例如,使用将阻塞从当前线程卸载到线程池中的某个线程)。但这仍然是一个阻塞操作:只是阻塞另一个线程。

    您的使用不是异步的——您在后台线程中启动任务,但主线程只是坐在那里等待。直接返回任务或等待任务或类似任务。抽象类中的方法根据定义是同步的,因此除非您可以更改,否则您的运气不好。请参阅TaskCompletionSource发布实际的数据访问代码,而不是包装器。ADO.NET具有异步方法,如
    ExecuteReaderAsync
    ExecuteOnQueryAsync
    。像EF这样的orm和像Dapper这样的microORMs也提供异步方法。最有可能的情况是,您的实际数据访问代码可以修改为异步运行。Re:您的更新-您的主线程仍在阻止此方法,并且在方法签名更改之前,它始终处于阻塞状态。你能解释一下你在这里想要实现什么吗?你只是想避免阻塞一个UI线程,还是你真的期望OS级异步?好吧,正如Richard的回答所指出的,你不能通过包装方法来实现OS级异步——这不会改变幕后发生的事情。为此,您需要使用异步API。关于不阻塞UI线程,为了实现这一点,您需要更改
    GetData
    的方法签名,以便它也返回一个任务(通过直接返回包装的任务或使用async/await)-你需要一直这样做直到你的UI,否则UI线程仍然会被阻塞。我认为这里的术语有点模糊-我想包装后台线程的任务可以被认为是异步的(至少从消费者的角度来看)@AntP不完全-用任务包装阻塞调用就是所谓的伪异步。异步(不等待完成)与并发(并行执行许多事情)不同。Run将启动运行同步作业的并发任务。但是,使用BeginExecute或等效的异步方法,将请求传递到异步驱动程序级别,并且根本不使用线程。这对于web应用程序或高吞吐量来说是一个巨大的好处servers@PanagiotisKanavos我理解这一点,但对于使用者来说,语义问题无关紧要——特别是考虑到返回任务的方法的典型命名约定要求它们附加异步,而不是并发,在这个上下文中,任务在最低级别是否是异步的无关紧要。@AntP这根本不是语义问题。这是一个根本性的区别,从一个非常基本的方面来说,它确实对消费者很重要。这意味着
    Task.Run(()=>cmd.ExecuteReader())
    wait cmd.ExecuteReaderAsync()
    有着根本的不同。最后,这意味着如果正确使用异步编程,同一台服务器可以处理更多的请求(倍数,而不是百分比)。它还带来了更干净、更安全的代码。@PanagiotisKanavos再一次-我已经理解了所有这些。我的意思是,“异步”被广泛地(并且通常是明确地)用于特定的上下文(例如,如果它不阻塞的话)