Как я могу получить результат моей работы в задании - PullRequest
0 голосов
/ 28 мая 2018

Мне нужно выполнить работу в Задании (бесконечный цикл для мониторинга), но как я могу получить результат этой работы?

Моя логика делать все это, я ошибаюсь?Я думаю, что это проблема области видимости.

Есть упрощенный пример: переменная "first", и я хочу "edit"

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;
        }
    }
}

Ответы [ 3 ]

0 голосов
/ 28 мая 2018

Это можно сделать, используя пользовательский event.В вашем случае это может быть что-то вроде:

public event Action<string> OnValueChanged;

Затем присоедините к нему

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

И не забудьте запустить его

this.test = "edit";
OnValueChanged?.Invoke(this.test);
0 голосов
/ 28 мая 2018

Задачи не являются потоками, для их запуска не требуется вызов .Start.Все примеры и учебные пособия показывают использование Task.Run или Task.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 показывает, как использовать 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;

В этом случае цикл завершится при поднятии CancellationToken.Не ожидая MyMonitor.Run(), мы можем продолжать работать с основным потоком, пока не произойдет событие, при котором мониторинг сигналов должен прекратиться.

0 голосов
/ 28 мая 2018

Метод 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, проверьте эту ссылку .

...