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