如何在任务中获取我的工作结果



我需要在任务中做一个工作(用于监控的无限循环(,但我如何获得这项工作的结果?

我做这些事情的逻辑我错了?我认为这是一个范围问题。

有一个简化的示例: 变量是"第一个",我想要"编辑">

namespace my{
public class Program{
public static void Main(string[] args){
Logic p = new Logic();
Task t = new Task(p.process);
t.Start();
Console.WriteLine(p.getVar());// result="first"  
}
}
public class Logic{
public string test = "first";
public void process(){
while(true){
//If condition here
this.test = "edit";
}
}
public String getVar(){
return this.test;
}
}
}

可以使用自定义event来完成。在您的情况下,它可以是这样的:

public event Action<string> OnValueChanged;

然后附加到它

p.OnValueChanged += (newValue) => Console.WriteLine(newValue);

并且不要忘记发射它

this.test = "edit";
OnValueChanged?.Invoke(this.test);

任务不是线程,它们不需要.Start调用即可启动它们。所有示例和教程都显示了使用Task.RunTask.StartNew是有原因的 - 任务是函数将在将来某个时候执行并产生结果的承诺。当任务计划程序决定它们应该运行时,它们将在从 ThreadPool 拉取的线程上运行。创建冷任务和调用.Start并不能保证它们会启动,它只会使代码更难阅读。

在最简单的情况下,轮询(例如远程 HTTP 端点(可以像以下那样简单:

public static async Task Main()
{
var client=new HttpClient(serverUrl);
while(true)
{
var response=await client.GetAsync(relativeServiceUrl);
if(!response.IsSuccessStatusCode)
{
//That was an error, do something with it
}
await Task.Delay(1000);
}
}

无需启动新任务,因为GetAsync是异步的。WCF 和 ADO.NET 还提供异步执行方法。

如果没有要调用的异步方法,或者我们需要在异步调用之前执行一些繁重的工作,则可以使用 Task.Run 并行启动方法并等待它完成:

public bool CheckThatService(string serviceUrl)
{
....
}
public static async Task Main()
{
var url="...";
//...
while(true)
{
var ok=Task.Run(()=>CheckThatService(url));
if(!ok)
{
//That was an error, do something with it
}
await Task.Delay(1000);
}
}

如果我们想并行测试多个系统怎么办?我们可以并行启动多个任务,等待所有任务完成并检查其结果:

public static async Task Main()
{
var urls=new[]{"...","..."};
//...
while(true)
{
var tasks=urls.Select(url=>Task.Run(()=>CheckThatService(url));
var responses=await Task.WhenAll(tasks);
foreach(var response in responses)
{
///Check the value, due something
}
await Task.Delay(1000);
}
}

Task.WhenAll返回一个数组,其中结果按任务的创建顺序排列。这允许检查索引以查找原始 URL。更好的主意是一起返回结果和 url,例如使用元组:

public static (bool ok,string url) CheckThatService(string serviceUrl)
{
....
return (true,url);
}

代码不会有太大变化:

var tasks=urls.Select(url=>Task.Run(()=>CheckThatService(url));
var responses=await Task.WhenAll(tasks);
foreach(var response in responses.Where(resp=>!resp.ok))
{
///Check the value, due something
}

如果我们想存储所有调用的结果怎么办?我们不能使用列表或队列,因为它们不是线程安全的。我们可以改用 ConcurrentQueue:

ConcurrentQueue<string> _results=new ConcurrentQueue<string>();
public static (bool ok,string url) CheckThatService(string serviceUrl)
{
....
_results.Enqueue(someresult);
return (true,url);
}

如果我们想定期报告进度,可以使用IProgress<T>,如在异步 API 中启用进度和取消中所示。

我们可以将所有监视代码放在一个单独的方法/类中,该方法/类接受带有进度对象的IProgress< T>参数,该参数可以报告成功、错误消息和导致它们的 URL,例如:

class MonitorDTO
{
public string Url{get;set;}
public bool Success{get;set;}
public string Message{get;set;}        
public MonitorDTO(string ulr,bool success,string msg)
{
//...
}
}
class MyMonitor
{
string[] _urls=url;
public MyMonitor(string[] urls)
{
_urls=url;
}   
public Task Run(IProgress<MonitorDTO> progress)
{
while(true)
{
var ok=Task.Run(()=>CheckThatService(url));
if(!ok)
{
_progress.Report(new MonitorDTO(ok,url,"some message");
}
await Task.Delay(1000);
}
}
}

此类可以按以下方式使用:

public static async Task Maim()
{
var ulrs=new[]{....};
var monitor=new MyMonitor(urls);
var progress=new Progress<MonitorDTO>(pg=>{
Console.WriteLine($"{pg.Success} for {pg.Url}: {pg.Message}");
});
await monitor.Run(progress);
}

在异步 API 中启用进度和取消演示如何使用 CancelTokenSource 实现监视类的另一个重要部分 - 取消它。监视方法可以定期检查取消令牌的状态,并在引发时停止监视:

public Task Run(IProgress<MonitorDTO> progress,CancellationToken ct)
{
while(!ct.IsCancellationRequested)
{
//...
}
}
public static async Task Maim()
{
var ulrs=new[]{....};
var monitor=new MyMonitor(urls);
var progress=new Progress<MonitorDTO>(pg=>{
Console.WriteLine($"{pg.Success} for {pg.Url}: {pg.Message}");
});
var cts = new CancellationTokenSource();
//Not awaiting yet!
var monitorTask=monitor.Run(progress,cts.Token);
//Keep running until the first keypress
Console.ReadKey();
//Cancel and wait for the monitoring class to gracefully stop
cts.Cancel();        
await monitorTask;

在这种情况下,当提出取消令牌时,循环将退出。通过不等待MyMonitor.Run()我们可以继续在主线程上工作,直到发生信号监控应该停止的事件。

getVar方法在process方法之前执行。 请确保等到任务完成后再调用getVar方法。

Logic p = new Logic();
Task t = new Task(p.process);
t.Start();
t.Wait(); // Add this line!
Console.WriteLine(p.getVar());

如果您想了解有关Wait方法的更多信息,请查看此链接。

最新更新