如何在任务运行时将一些数据从TPL任务传播到主进程

本文关键字:任务 TPL 传播 数据 进程 运行时 | 更新日期: 2023-09-27 18:13:04

我有一种情况,我创建了一个长时间运行的任务列表,它监视一些系统/网络资源,然后发送电子邮件日志到一个txt文件,并且调用web服务当某些条件得到满足。然后再次开始监控。这些任务是在windows服务中创建的,因此将一直运行。

我希望它们引发事件或其他东西来通知父类(创建它们的类),它将执行上面提到的3个操作,而不是任务中的每个对象自己执行。

以及如何控制只有一个任务在一个时间使用父类的方法。由于涉及电子邮件和web服务调用,因此两个并发请求可能会影响代码。

这些观察者有三种类型,每种类型实现以下接口。

public interface IWatcher
{
    void BeginWatch();        
}

实现的类是

//this watcher is responsible for watching over a sql query result
public class DBWatcher : IWatcher
{
    ....
    void BeginWatch()
    {
        //Here a timer is created which contiously checks the SQL query result.
        //And would Call SERVICE, send an EMAIL and LOG into a file
        Timer watchIterator = new Timer(this._intervalMinutes * 60000);
        watchIterator.Elapsed += new ElapsedEventHandler(_watchIterator_Elapsed);
        watchIterator.Start();
    }
    void _watchIterator_Elapsed(object sender, ElapsedEventArgs e)
    {
        //1. Check Query result
        //3. Call SERVICE, send an EMAIL and LOG into a file if result is not as was expected
        //I have done the work to this part!
        //And I can do the functions as follows .. it should be simple.
        //*********************
        //SendEmail();
        //LogIntoFile();
        //CallService();
        //But I want the above three methods to be present in one place so i dont have to replicate same functionality in different watcher.
        //One approach could be to create a seperate class and wrape the above mentioned functions in it, create an instance of that class here and call them.
        //Second option, which I am interested in but dont know how to do, is to have this functionality in the parent class which actually creates the tasks and have each watcher use it from HERE ...
    }
    ....
}

//this watcher is responsible for watching over Folder
public class FolderWatcher : IWatcher
{
    ....
    void BeginWatch()
    {
         ///Same as above
    }
    ....
}

首先,我从一个XML文件创建一个List。这可以包含DBWatcher的多个实例,DBWatcher将连续观察不同的查询结果,FolderWatcher将连续观察不同的文件夹。

创建List之后,我调用下面的函数来创建一个单独的Task。我多次调用这个函数来创建一组不同的观察者。

    private void _createWatcherThread(IWatcher wat, CancellationTokenSource cancellationToken)
    {
        //This represents a watcher that will watch some specific area for any activities
        IWatcher watcher = wat.Copy();
        bool hasWatchBegin = false;
        try
        {
            //run forever
            for (;;)
            {
                //dispose the watcher and stop this thread if CANCEL token has been issued
                if (cancellationToken.IsCancellationRequested)
                {
                    ((IDisposable)watcher).Dispose();
                    break;
                }
                else if (!hasWatchBegin)
                {
                    //This method of a watcher class creates a timer. which will
                    //continously check the status after a few minutes... So its the 
                    //timer's elapsed method in Watcher object which will send the mail 
                    //& call service etc to update the admin of current status of the watcher.
                    //this will be called only once in a watcher!
                    watcher.BeginWatch();
                    hasWatchBegin = true;
                }
            }
        }
        catch (Exception ex)
        {
            //Watcher has thrown an exception. 
            //Again, do the following operations
            //*********************
            //SendEmail();
            //LogIntoFile();
            //CallService();
        }
    }

如何在任务运行时将一些数据从TPL任务传播到主进程

如果您的电子邮件,日志&webservice调用threadsafe,你可以将代码的引用作为闭包(这里是Jon Skeet对c#闭包的精彩解释)传递到监控任务中。下面是一个需要启动多个任务的示例:

...
void Email(string message){}
void Log(string message){}
void CallWebService(string message){}

void RunMonitoringTask()
{
    var task = Task.TaskFactory.StartNew(() =>
    {
        string message = Monitor();
        if( ShouldNotify(message))
           {
              Email(mesasge);
              Log(message);
              CallWebService(message);
           }
    }
)
}
...

编辑

vs。必要时触发任务的无限监视器循环:

...
void Email(string message){}
void Log(string message){}
void CallWebService(string message){}

void Monitor()
{
    while(true)
    {
          string message = Monitor();
          if(ShouldNotify(message))
          { 
              var task = Task.TaskFactory.StartNew(() =>
              {
                  Email(mesasge);
                  Log(message);
                  CallWebService(message);
              }
         }
    }
)
}
...

至于如何实现这些类,我建议采用一种方法,其中每个接收器都接受消息&然后卸载到它自己的处理线程/任务,以避免阻塞您的监控任务&显示其他通知

Progress类非常适合这项任务。它是一种允许长时间运行的流程通知某人(通常是调用者)该操作的当前进度的方法。

Progress<string> progress = new Progress<string>();
progress.ProgressChanged += (s, data) => Console.WriteLine(data);
for (int i = 0; i < 2; i++)
    Task.Run(() => DoWork(progress));

public static void DoWork(IProgress<string> progress)
{
    int i = 0;
    while (true)
    {
        Thread.Sleep(500);//placeholder for real work
        progress.Report(i++.ToString());
    }
}

如果您在不同的时间报告不同类型的信息,那么只需将多个IProgress实例传递给worker方法。(或者,如果您同时报告几种类型数据的进度,则将所有数据包装在复合对象中。)

还要注意,这能够处理您所说的需要的同步。Progress实例在创建时捕获SynchronizationContext.Current的值,并将进程更改事件的所有事件处理程序编组到该同步上下文中。所以如果你的应用程序已经有了一个上下文(例如桌面应用程序的UI上下文),那么你就可以免费得到它。如果你没有一个(例如,它是一个控制台应用程序),那么你需要手动同步事件处理程序,说lock,或创建自己的SynchrnonizationContext设置为当前上下文。