C# 我怎样才能在一项任务中得到工作的结果

C# 我怎样才能在一项任务中得到工作的结果,c#,task,C#,Task,我需要在一个任务中做一项工作(无限循环用于监控),但我如何才能得到这项工作的结果 我做这件事的逻辑我做错了?我认为这是一个范围问题 有一个简化的例子: 变量是“first”,我想要“edit” getVar方法在过程方法之前执行。 确保在调用getVar方法之前等待任务完成 Logic p = new Logic(); Task t = new Task(p.process); t.Start(); t.Wait(); // Add this line! Console.WriteLine(p.

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

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

有一个简化的例子: 变量是“first”,我想要“edit”


getVar
方法在
过程
方法之前执行。 确保在调用
getVar
方法之前等待任务完成

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

如果您想了解有关
等待
方法的更多信息,请检查。

可以使用自定义
事件来完成。在您的情况下,它可能类似于:

public event Action<string> OnValueChanged;
别忘了开枪

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

任务不是线程,它们不需要调用
.Start
来启动它们。所有示例和教程都说明了
Task.Run
Task.StartNew
的用法,原因是任务承诺函数将在将来某个时间执行并产生结果。当任务调度器决定它们应该运行时,它们将在从线程池中提取的线程上运行。创建冷任务并调用
.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.whalll
按创建任务的顺序返回结果数组。这允许检查索引以查找原始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<string> _results=new ConcurrentQueue<string>();

public static (bool ok,string url) CheckThatService(string serviceUrl)
{
    ....
    _results.Enqueue(someresult);
    return (true,url);
}
ConcurrentQueue_results=new ConcurrentQueue();
公共静态(bool ok,字符串url)检查服务(字符串服务url)
{
....
_结果:排队(someresult);
返回(true,url);
}
如果我们想定期报告进度,我们可以使用
IProgress
,如中所示

我们可以将所有监控代码放在一个单独的方法/类中,该方法/类接受带有进度对象的
IProgress
参数,该进度对象可以报告成功、错误消息以及导致它们的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);
      }
    }
}
类监视器
{
公共字符串Url{get;set;}
公共bool成功{get;set;}
公共字符串消息{get;set;}
公共监视器DTO(字符串ulr、bool success、字符串msg)
{
//...
}
}
类MyMonitor
{
字符串[]url=url;
公共MyMonitor(字符串[]URL)
{
_url=url;
}   
公共任务运行(IProgress进程)
{
while(true)
{
var ok=Task.Run(()=>CheckThatService(url));
如果(!ok)
{
_progress.Report(新的MonitorDTO(ok,url,“一些消息”);
}
等待任务。延迟(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);
}
公共静态异步任务Maim()
{
var ulrs=新[]{….};
var monitor=新的MyMonitor(URL);
变量进度=新进度(pg=>{
WriteLine($“{pg.Url}:{pg.Message}的{pg.Success}”);
});
等待监视器。运行(进度);
}
演示如何使用CancellationTokenSource实现监视类的另一个重要部分-取消它。监视方法可以定期检查取消令牌的状态,并在引发时停止监视:

    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;
公共任务运行(IProgress progress,CancellationToken ct)
{
而(!ct.iscancellationrequest)
{
//...
}
}
公共静态异步任务Maim()
{
var ulrs=新[]{….};
var monitor=新的MyMonitor(URL);
变量进度=新进度(pg=>{
WriteLine($“{pg.Url}:{pg.Message}的{pg.Success}”);
});
var cts=新的CancellationTokenSource();
//还没等呢!
var monitorTask=monitor.Run(进度,cts.Token);
//继续运行,直到第一次按键
Console.ReadKey();
//取消并等待监视类正常停止
cts.Cancel();
等待监控任务;

在这种情况下,当发出CancellationToken时,循环将退出
我们可以继续在主线程上工作,直到发生信号监视应该停止的事件为止。

您正在启动任务,但不是等待它完成。不过,您的描述与代码不匹配-在这种情况下,等待任务完成是有意义的,但在您描述无限长l的情况下则没有意义oop。无限循环在什么时候会提供结果?首先,你没有真正解释你想要什么。你希望无限循环产生什么样的结果?可能是通知?第一个成功的结果?第二,任务不是线程。你不需要
.Start
来启动它们。只需使用
Task.Run(somethod)
。您可以等待它完成
等待任务。运行(…)
并使用
var result=await Task.Run(…)分配任何结果
@Steve你想做什么?轮询服务并记录每个结果?或轮询服务并记录第一次故障?你想在第一次故障后继续循环吗?这是用于网络监控的,因此可以在循环中以不规则的间隔获取帧。这些帧存储在列表中,与我的字符串var相同在我的例子中。这就是为什么我不能等待任务的结束。事实上,我希望
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);
}
    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;