了解如何在不使用多线程的情况下使用Async

时间:2017-05-26 19:48:12

标签: c# multithreading asynchronous

我一直在阅读异步方法,特别是在C#中使用新的async / await关键字,尽管阅读和阅读了这个论坛,我仍然相信异步需要多线程。请解释一下我的误解!

我知道您可以编写异步方法而不会生成后台线程。超级基本的例子:

data_path = '/Users/abs/Desktop/data'
data_dir_list = os.listdir(data_path)

for dataset in data_dir_list:
    if os.path.isdir(os.path.join(data_path, dataset): # <-- if it is a directory
        img_list=os.listdir(data_path+'/'+dataset)
        print ('Loaded the images of dataset-'+'{}\n'.format(dataset))

但是,当然,这种方法完全没用,无法标记为异步,因为它不是异步。我还得到一个编译器警告,说明缺少&#34; await&#34;运营商,正如所料。好的,那我们可以等待什么呢?我可以等待像Task.Run()这样的东西,但这就失败了,因为我现在正在使用多线程。我在网上找到的任何其他例子都试图证明你不需要多线程,只需这样做:

async System.Threading.Tasks.Task<int> GetZeroAsync()
{
    return 0;
}

也许我误解了这一点,但它向我证明的是我&#m> 不是那个启动多线程任务的人,而是我只是打电话给另一个人做的方法。由于CallAnotherAsyncMethod()也是一个异步方法,它必须遵循完全相同的规则,对吗?我不能让每个异步方法永远等待另一个异步子方法,在某些时候它必须停止,除非你想要无限递归。

我目前理解它的方式,我知道这是错误的,是async不使用多线程,但它需要它,否则它只是一个同步方法骗你。

所以这可能会有所帮助。如果异步真的不需要多线程,则必须能够生成以下情况,我无法找到实现它的方法。有人可以创建遵循这些规则的方法示例:

  1. 异步。

  2. 实际上是异步运行。

  3. 不使用任何多线程,如调用Task.Run()或使用BackgroundWorker等。

  4. 不会调用任何其他异步方法(除非您也可以证明此方法也遵循这些规则)。

  5. 不会像Task.Delay()一样调用Task类的任何方法(除非你也可以证明这个方法也遵循这些规则)。

  6. 任何帮助或澄清都会非常有帮助!我觉得自己是个不理解这个话题的白痴。

1 个答案:

答案 0 :(得分:2)

不使用任何线程的异步操作的最简单示例是等待事件发生。

使用您选择的框架创建一个UI应用,并有两个按钮,一个名为PrimeButton,一个名为RunButton

private TaskCompletionSource<object> _event = new TaskCompletionSource<object>();

//You are only allowed to do async void if you are writing a event handler!
public async void PrimeButton_OnClick(object sender, EventArgs e)
{
    //I moved the code in to Example() so the async void would not be a distraction.
    await Example();
}

public async Task Example()
{
    await _event.Task;
    MessageBox.Show("Run Clicked");
}

public async void RunButton_OnClick(object sender, EventArgs e)
{
    _event.SetResult(null);
}

await将等到您单击第二个按钮,然后才允许代码继续并显示消息框。这里没有额外的线程,所有工作都只使用UI线程完成。

所有Task是一个对象,代表&#34; 将来某个时候完成的事情&#34;。有些东西可能正在等待后台线程完成,这是由Task.Run启动的,或者它可能正在等待.SetResult(上的TaskCompletionSource<T>调用函数,或者它可以等待某种磁盘或网络IO完成并由OS读入缓冲区(但内部通常通过内置TaskCompletionSource<T>隐藏在ReadAsync()函数内部实现,所以它只是重复最后一个例子,周围有一个包装器)