如何处理一个等待的任务?

时间:2016-04-20 20:04:39

标签: c#

我是“任务”中的新手,仍然在学习这个主题,所以对我很温柔(我想我的下面的代码有一些基本的混乱......)

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

换句话说,假设我有2个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,它运行良好,但只有2个实例异步运行。 “运行良好”意味着在停止任务时,它会通过运行整个“Do_CPU_Intensive_Job”方法很好地停止。因此最后一条消息将来自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");
    }
}

当2个实例未同时运行时,问题开始。在这种情况下,我在GUI 之前完成了“两个任务已经完成......现在做了一些其他的事情”“Do_CPU_Intensive_Job”真的完成了......

        ms1 = new MyService("task 1", false);
        ms2 = new MyService("task 2", false);

当两个任务一起运行时没有发生这种情况,因为我在异步运行时有句柄(myTask),而在同步运行时我没有。

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

谢谢,所有

1 个答案:

答案 0 :(得分:0)

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

我发现的第一个问题是你不能只将取消令牌传递给你的方法,你需要将它与要取消的任务联系起来。很遗憾,我无法直接在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_CPU_Intensive_Job,如果您注意到我将令牌传递给工作方法和Task.Run调用。后者将令牌链接到该任务/线程,前者是允许我们监视取消请求的原因。

最后一部分是我们如何在服务实例上调用Run,通过在Task或异步方法上调用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();