异步运行两个任务

3

我正在运行一个同步方法。在这个方法里,我需要运行两个大的方法,所以我想要将它们异步地运行。我考虑了一些东西:

public void MyFunc()
{
    var doWorkTask_1 = DoWork1();
    var doWorkTask_2 = DoWork2();

    var result1 = await doWorkTask_1;
    var result2 = await doWorkTask_2;

    if(result1 == result2) 
       ....

    Thread.Sleep(syncInterval);
}

为了做到这一点,我需要以下内容:
  1. DoWork1DoWork2是异步的;
  2. MyFunc也是异步的;
但是没有任何方法是异步的!!!
所以我尝试用另一种方式:
public void MyFunc()
{
    var doWorkTask_1 = Task.Run(() => DoWork1());
    var doWorkTask_2 = Task.Run(() => DoWork2());

    var result1 = doWorkTask_1.Result;
    var result2 = doWorkTask_2.Result;

    if(result1 == result2) 
       ....

    Thread.Sleep(syncInterval);
}

那么,第一个问题:

我是否以两种不同的方式写了同样的事情?

第二个问题。我需要每隔X时间运行MyFunc方法,所以我这样调用它:

Task.Factory.StartNew(MyFunc);

我可以简单地称它为

MyFunc();

我的问题是,因为在myFunc内部有一个Thread.Sleep,我是否可以让主线程休眠,还是最好让主线程内的线程休眠?

希望我表达清楚了。 谢谢。


你能把 MyFunc 改成异步的吗?还是必须要同步执行? - NeddySpaghetti
我更喜欢它同步,因为如果我将MyFunc设置为异步,我还必须对调用MyFunc异步的方法进行相应的更改,以此类推... - Simone
3个回答

6

我是否用两种不同的方式写了同一件事?

没有。您的第一种方法将以并行方式执行两个工作单元,并且将异步等待第一个,然后是第二个。

您的第二种方法将以并行方式执行两个工作单元,并且将同步等待第一个,然后是第二个。

我可以让主线程休眠,还是最好让主线程内部的线程休眠?

这取决于您的应用程序正在做什么。您可以将MyFunc变为async,以便可以使用Task.Delay,它在内部使用计时器并且不会阻塞(如果需要,还可以传递CancellationToken):

public async Task MyFuncAsync()
{
   // Do work

   await Task.Delay(syncInterval);
}

顺便提一下:

在我看来,你可能正在使用异步而非同步这通常是一个值得怀疑的方法。我建议不要这样做。

相反地,就像你第一个示例中那样,在这些工作程序上明确调用Task.Run

public async Task MyFuncAsync()
{
    var firstTask = Task.Run(() => DoWork1());
    var secondTask = Task.Run(() => DoWork2());

    await Task.WhenAll(new[] { firstTask, secondTask });
    await Task.Delay(syncInterval);
}

我能在Task.Run内部使用匿名函数吗?而且,我该如何调用MyFuncAsync?只需使用MyFuncAsync(); 或者 Task.Factory.StartNew(MyFuncAsync)? - Simone
1
@Cicco 你说得对。这就是为什么异步从堆栈的顶部一直到底部。这是一个问题吗? - Yuval Itzchakov
2
@Ciccio 不行,但你可以使用初始化模式 - Yuval Itzchakov
1
@Ciccio 那你会被阻塞。这可能导致死锁。我建议你不要这样做。 - Yuval Itzchakov
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Simone
显示剩余6条评论

2
使用 Task.WhenAll 创建一个新的任务,封装你的工作任务。
创建一个任务,当所有提供的任务都完成时,该任务将完成。 https://msdn.microsoft.com/en-us/library/hh194874%28v=vs.110%29.aspx
public async void MyFunc()
{
    var doWorkTask_1 = DoWork1();
    var doWorkTask_2 = DoWork2();

    var results = await Task.WhenAll(doWorkTask_1, doWorkTask_2);
}

1
但是您需要将 MyFunc 也设置为异步,这显然不是 OP 想要做的。 - NeddySpaghetti
1
@NedStoyanov 我犯这个错误的频率让编译器开始嘲笑我了。。 - Gusdor
你的意思是 var doWorkTask_1 = new Task( () => { DoWork1();}); 吗? - Simone
@Gusdor 我经常犯同样的错误。我认为SO应该弹出一个警告 :) - NeddySpaghetti

1
如果您无法完全使用async,并且异步意味着您想在不同的线程上同时处理DoWork1DoWork2,那么您可以使用Task.Run将工作移交给另一个线程,并使用Task.WaitAll等待同步地完成两个任务:
public void MyFunc()
{
    var task1 = Task.Run(() => DoWork1());
    var task2 = Task.Run(() => DoWork2());

    Task.WaitAll(task1, task2);
    if (task1.Result == task2.Result)
    {
        // ...
    }

    Thread.Sleep(syncInterval);
}

现在,由于这个示例使用了3个线程(两个ThreadPool线程在Task.Run中,以及调用线程被阻塞在Task.WaitAll上),而我们只需要2个线程,因此我们可以简化和优化示例,通过在调用线程上执行其中一个操作来实现。
public void MyFunc()
{
    var task1 = Task.Run(() => DoWork1());
    var result2 = DoWork2();

    if (task1.Result == result2)
    {
        // ...
    }

    Thread.Sleep(syncInterval);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接