【问题标题】:How to wait for a thread is finished before execute next code?如何在执行下一个代码之前等待线程完成?
【发布时间】:2019-08-05 08:35:47
【问题描述】:

我需要这样的代码:

module.StopAndWait();
module.DoSmth();
module.Resume();

我已经编写了 AsyncModel 类。我对“while (!isStopped);”行有疑问。在这一行,程序冻结。我认为 isStopped 必须改变,但事实并非如此。 我试图将 isStopped 声明为 volatile。 但无论如何程序在“while (!isStopped);”上有问题。

public class AsyncModel
{
    bool run = false;
    bool isStopped;
    public async void Run()
    {
        if (run) return;
        await Task.Run(() => progr());
    }
    public void StopAndWait()
    {
        run = false;
        while (!isStopped) ;// <-- Problem
    }
    void progr()
    {
        isStopped = false;
        run = true;

        while (run)
        {
            Thread.Sleep(1500);
        }

        isStopped = true;
    }
}

【问题讨论】:

  • 当你使用异步方法时你想要并行,否则使用同步方法。
  • 将 t 或 f 分配给 isstopped
  • volatile bool isStopped;,试试这个,run 字段也是如此。创建自己的线程信号是一个坏主意,.NET 中已经有很好的类,请查看 ManualResetEvent/AutoResetEvent,理想情况下,您可能应该使用 CancellationToken 等完全使用任务。resume 看起来喜欢吗?
  • 存在多个问题(例如,async void 的使用以及由于使用没有同步的变量而导致的竞争条件)。主要问题是没有任何东西调用Run 所以StopAndWait 永远不会结束。如果要等待任务终止,请使用await。所有这些代码都可以替换为单个await Task.Run(()=&gt;progr())。如果你想在等待之前做点什么,var myTask=Task.Run(...); Something(); await myTask;.
  • 发出取消信号的正确方法是使用 CancellationTokenSource 并将 CancellationToken 传递给 progr。检查Enabling Progress and Cancellation in Async APIs

标签: c# async-await


【解决方案1】:

我假设这段代码试图运行一个可以在后台取消的循环。 执行此操作的简单方法是使用 CancellationTokenSource 和 CancellationToken,例如:

void progr(CancellationToken token)
{
    while(!token.IsCancellationRequested)
    {
        // Do something expensive
    }
}

注意progr 没有引用外部字段。它所需要的只是token

应在任务本身开始之前创建 CancellationTokenSource。

Task thatTask;
CancellationTokenSource cts;

public void StartTheJob()
{
    cts=new CancellationTokenSource();
    thatTask=Task.Run(()=>progr(cts.Token));
}

要停止任务,只需调用cts.Cancel()

public async Task StopTheJob()
{
    cts.Cancel();
    //Wait for the cancelled task to exit
    await thatTask;
}

要添加延迟,请使用 await Task.Delay(...) 而不是 `Thread.Sleep()。 Task.Delay 可以取消并且不会阻塞用于运行任务的线程池线程:

async Task progr(CancellationToken token)
{
    while(!token.IsCancellationRequested)
    {
        // Do something expensive
        await Task.Delay(1000,token);
    }
}

查看Async in 4.5: Enabling Progress and Cancellation in Async APIs了解更多信息

【讨论】:

  • await Task.Delay(1000, token); 这可能会引发一个TaskCanceledException 应该被捕获和处理。
  • @TheodorZoulias 我知道。如果有人知道它的实际目的是什么,则可以为此添加更多代码。可以使用 timer 来执行循环作业而不是循环。尽管(间接地)询问了如何正确取消后台任务的问题。如果有人问如何在后台投票,一个更好的主意是计时器或类似的东西,而不是循环
  • 我认为这是一个有趣的问题,但已经被问到:Cancel task.delay without exception or use exception to control flow?
  • 我想知道为什么run = false; 会影响while(run)...isStopped = true; 不会影响while (!isStopped) ;
  • @RomanAleshin 任何从另一个线程修改全局状态的尝试都需要同步以避免随机结果,但在这种情况下,async void 是一个额外的严重问题 - async void 仅适用于事件处理程序。这意味着该方法是一个无法等待的fire-and-forget 方法,并且该任务可能在完成之前被垃圾收集。甚至无法保证任务在应用程序退出之前启动。 isStopped = true; 行可能根本不会运行。请改用async Task,并将任务至少存储在变量或字段中
【解决方案2】:

看来您的问题可以通过使用普通的旧同步代码轻松解决。另外,请尝试使用Task.Delay() 而不是Thread.Sleep。最后,方法命名的一致性。

【讨论】:

    猜你喜欢
    • 2021-07-30
    • 2020-09-28
    • 2018-05-20
    • 1970-01-01
    • 1970-01-01
    • 2020-04-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多