C# 如何处理等待的任务?

C# 如何处理等待的任务?,c#,C#,我是一个任务新手,仍然在学习这个主题,所以请对我温和一点(我想我下面的代码有一些基本的混乱…) 请看下面的练习,它将帮助我描述我的问题: 我有一个简单的“MyService”类,它有一个由“Run”方法调用的“Do\u CPU\u-Intensive\u-Job”方法。我的目的是能够运行“Do_CPU_Ensensitive_Job”方法的多个实例(该方法本身在与CPU绑定的UI不同的线程上运行),有时同步,有时异步 await Do_CPU_Intensive_Job(); // I cann

我是一个任务新手,仍然在学习这个主题,所以请对我温和一点(我想我下面的代码有一些基本的混乱…)

请看下面的练习,它将帮助我描述我的问题: 我有一个简单的“MyService”类,它有一个由“Run”方法调用的“Do\u CPU\u-Intensive\u-Job”方法。我的目的是能够运行“Do_CPU_Ensensitive_Job”方法的多个实例(该方法本身在与CPU绑定的UI不同的线程上运行),有时同步,有时异步

await Do_CPU_Intensive_Job(); // I cannot do myTask = await Do_CPU_Intensive_Job(); so how can the "Stop" method wait for it??
换句话说,假设我有两个MyService实例,有时我希望这两个方法一起运行,有时不希望

class MyService
{
    private bool async;
    private string name;
    private CancellationTokenSource tokenSource;
    private CancellationToken token;
    private bool isRunning = false;
    private Task myTask = null;

    public MyService(string name, bool async)
    {
        this.name = name;
        this.async = async;
    }

    public string Name { get { return name; } }
    public bool IsRunning { get { return isRunning; } }

    public async Task Run ()
    {
        isRunning = true;
        tokenSource = new CancellationTokenSource();
        token = tokenSource.Token;

        if (async)
            myTask = Do_CPU_Intensive_Job();
        else
            await Do_CPU_Intensive_Job();     // I cannot do myTask = await Do_CPU_Intensive_Job(); so how can the "Stop" method wait for it??
    }

    public async Task Stop ()
    {
        tokenSource.Cancel();

        if (myTask != null)
            await myTask;

        isRunning = false;
    }

    private async Task Do_CPU_Intensive_Job ()
    {
        Console.WriteLine("doing some heavy job for Task " + name);
        int i = 0;
        while (!token.IsCancellationRequested)
        {
            Console.WriteLine("Task: " + name + " - " + i);
            await Task.Delay(1000);
            i++;
        }

        Console.WriteLine("Task " + name + " not yet completed! I need to do some cleanups");

        await Task.Delay(2000); //simulating cleanups
        Console.WriteLine("Task " + name + " - CPU intensive and cleanups done!");
    }
}
因此,我有下面的GUI,它工作得很好,但只有在这两个实例异步运行的情况下。“工作良好”意味着当停止任务时,它可以通过运行整个“Do_CPU_密集型_作业”方法很好地停止。因此,最后一条消息将来自GUI(“两个任务都完成了……现在正在做一些其他事情”)。到目前为止还不错

public partial class Form1 : Form
{
    List<MyService> list = null;
    MyService ms1 = null;
    MyService ms2 = null;

    public Form1()
    {
        InitializeComponent();
        list = new List<MyService>();
        ms1 = new MyService("task 1", true);
        ms2 = new MyService("task 2", true);
        list.Add(ms1);
        list.Add(ms2);
    }

    private async void button1_Click(object sender, EventArgs e)
    {
        foreach (MyService item in list)
            await item.Run();
    }

    private async void button2_Click(object sender, EventArgs e)
    {
        foreach (MyService item in list)
        {
            if (item.IsRunning)
            {
                await item.Stop();
                Console.WriteLine("Done stopping Task: " + item.Name);
            }
        }

        //now ready to do some other stuff
        Console.WriteLine("Both tasks are completed...now doing some other stuff");
    }
}
这在两个任务同时运行时不会发生,因为异步运行时我有句柄(myTask),而同步运行时我没有句柄(myTask)

await Do_CPU_Intensive_Job(); // I cannot do myTask = await Do_CPU_Intensive_Job(); so how can the "Stop" method wait for it??

谢谢,我花了一些时间把代码敲出来,我认为它正在做预期的事情

我发现的第一个问题是,您不能只将取消令牌传递到您的方法中,您需要将其与要取消的任务关联起来。不幸的是,我找不到直接在
async
方法上实现这一点的方法,但请看一下
MyService
类,了解我是如何做到这一点的

class MyService
{
    private bool async;
    private string name;
    private CancellationTokenSource tokenSource;
    private bool isRunning = false;
    private Task myTask = null;

    public MyService(string name, bool async)
    {
        this.name = name;
        this.async = async;
    }

    public string Name { get { return name; } }
    public bool IsRunning { get { return isRunning; } }

    public async Task Run()
    {
        isRunning = true;
        tokenSource = new CancellationTokenSource();

        myTask = Task.Run(() => Do_CPU_Intensive_Job(tokenSource.Token), tokenSource.Token);
        if (!async)
            await myTask;
    }

    public async Task Stop()
    {
        tokenSource.Cancel();

        if (myTask != null)
            await myTask;

        isRunning = false;
    }

    private void Do_CPU_Intensive_Job(CancellationToken token)
    {
        Console.WriteLine("doing some heavy job for Task " + name);
        int i = 0;
        while (!token.IsCancellationRequested)
        {
            Console.WriteLine("Task: " + name + " - " + i);
            Thread.Sleep(1000);
            i++;
        }

        Console.WriteLine("Task " + name + " not yet completed! I need to do some cleanups");

        Thread.Sleep(1000);
        Console.WriteLine("Task " + name + " - CPU intensive and cleanups done!");
    }
}
Run方法现在正在使用
Task.Run
调用
Do\u CPU\u Intensive\u Job
,如果您注意到我正在将令牌传递给工作方法和
任务。Run
调用。后者将令牌链接到该任务/线程,前者允许我们监视取消请求

最后一部分是我们如何在服务实例上调用Run,通过对任务或异步方法调用
await
,线程被释放,但方法中的剩余代码被提取,并且在等待的任务完成之前不会运行

我只是使用一个单元测试来处理代码,而不是一个按钮,但前提应该是一样的,但这里是我如何能够在同步模式下运行任务,并且仍然能够调用stop

var service1 = new MyService("task 1", false);
var service2 = new MyService("task 2", false);

service1.Run(); //Execution immediately moves to next line
service2.Run(); // Same here

await service1.Stop(); //Execution will halt here until task one has fully stopped so task 2 actually continues running
await service2.Stop();

我花了一些时间敲定代码,我认为它正在做预期的事情

我发现的第一个问题是,您不能只将取消令牌传递到您的方法中,您需要将其与要取消的任务关联起来。不幸的是,我找不到直接在
async
方法上实现这一点的方法,但请看一下
MyService
类,了解我是如何做到这一点的

class MyService
{
    private bool async;
    private string name;
    private CancellationTokenSource tokenSource;
    private bool isRunning = false;
    private Task myTask = null;

    public MyService(string name, bool async)
    {
        this.name = name;
        this.async = async;
    }

    public string Name { get { return name; } }
    public bool IsRunning { get { return isRunning; } }

    public async Task Run()
    {
        isRunning = true;
        tokenSource = new CancellationTokenSource();

        myTask = Task.Run(() => Do_CPU_Intensive_Job(tokenSource.Token), tokenSource.Token);
        if (!async)
            await myTask;
    }

    public async Task Stop()
    {
        tokenSource.Cancel();

        if (myTask != null)
            await myTask;

        isRunning = false;
    }

    private void Do_CPU_Intensive_Job(CancellationToken token)
    {
        Console.WriteLine("doing some heavy job for Task " + name);
        int i = 0;
        while (!token.IsCancellationRequested)
        {
            Console.WriteLine("Task: " + name + " - " + i);
            Thread.Sleep(1000);
            i++;
        }

        Console.WriteLine("Task " + name + " not yet completed! I need to do some cleanups");

        Thread.Sleep(1000);
        Console.WriteLine("Task " + name + " - CPU intensive and cleanups done!");
    }
}
Run方法现在正在使用
Task.Run
调用
Do\u CPU\u Intensive\u Job
,如果您注意到我正在将令牌传递给工作方法和
任务。Run
调用。后者将令牌链接到该任务/线程,前者允许我们监视取消请求

最后一部分是我们如何在服务实例上调用Run,通过对任务或异步方法调用
await
,线程被释放,但方法中的剩余代码被提取,并且在等待的任务完成之前不会运行

我只是使用一个单元测试来处理代码,而不是一个按钮,但前提应该是一样的,但这里是我如何能够在同步模式下运行任务,并且仍然能够调用stop

var service1 = new MyService("task 1", false);
var service2 = new MyService("task 2", false);

service1.Run(); //Execution immediately moves to next line
service2.Run(); // Same here

await service1.Stop(); //Execution will halt here until task one has fully stopped so task 2 actually continues running
await service2.Stop();


执行CPU密集型作业()。等待()
?@MeirionHughes感谢您的回复。你的意思是用.Wait()代替Wait吗?我不完全理解你的问题,但是是的,调用
.Wait()
肯定是我在
async/Wait
存在之前做过很多的事情。@MeirionHughes抱歉,但它不起作用。…@Jonathanalen谢谢你的回复。我的问题是,当停止时,我会在“Do_CPU密集型工作”完成之前返回GUI。在等待“执行CPU密集型作业”方法时发生。(ms1=new MyService(“task 1”,false);
Do\u CPU\u密集型作业().Wait()
?@MeirionHughes感谢您的回复。您的意思是用.Wait()代替Wait吗?我不完全理解您的问题,但是,是的,打电话给
.Wait())
肯定是我在
async/await
存在之前做了很多的事情。@MeirionHughes抱歉,但它不起作用……@jonathanalen感谢您的回复。我的问题是,当停止时,我会在“Do\u CPU\u密集型作业”完成之前回到GUI。在等待“Do\u CPU\u密集型作业”方法时发生。(ms1=new MyService)(“任务1”,false);谢谢,但在添加等待(令牌)时,Do_CPU_密集型_工作陷入困境,只进行了1次迭代…@RichardDeer我认为这可能是我的疏忽,我已编辑了我的答案以解决该疏忽。尝试一下更新版本。感谢您的努力!我已按照您的建议修改了代码,但在Do_CPU_密集型_工作中仍然只进行了第一次迭代。我ave通过向任务添加:.ConfigureWait(false)修复了此问题。延迟(1000)现在它正在运行,但是GUI被冻结了!所以我不能按停止按钮来测试它…@RichardDeer我想我能够让你的代码按照你的期望运行,我已经完全重新编写了我的答案,以显示我所做的更改,并希望解释原因。如果有什么不清楚,或者如果我完全偏离了你的期望,请让我告诉你知道:)谢谢你,Phaeze对你的支持!它现在正在运行。我会消化你所做的更改!干杯!谢谢,但是当添加等待(令牌)时,Do_CPU密集型的工作被卡住了,只做了一次迭代…@RichardDeer我想这可能是我的疏忽,我已经编辑了我对reso的回答