【问题标题】:how to propagate some data to main process from TPL tasks while tasks are running如何在任务运行时将一些数据从 TPL 任务传播到主进程
【发布时间】:2013-05-10 03:31:57
【问题描述】:

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

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

如何控制一次只有一个任务使用该父类的方法。由于涉及到电子邮件和 Web 服务调用,因此两个并发请求可能会破坏代码。

更新

这些Watcher分为三种类型,每种都实现了以下接口。

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 文件创建一个列表。这可以包含多个 DBWatcher 实例,它们将持续观察不同的查询结果,FolderWatcher 将持续观察不同的文件夹。

创建列表后,我调用以下函数来创建单独的任务。我多次调用这个函数来创建一组不同的观察者。

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

【问题讨论】:

    标签: c# multithreading task-parallel-library


    【解决方案1】:

    如果您使您的电子邮件、日志记录和 web 服务调用线程安全,您可以将引用作为闭包发送到每个接收器的代码(这里是 Jon Skeet 的 c# 闭包的excellent explanation)到监控任务中。这是您需要启动多个任务的示例:

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

    编辑

    对比必要时无限监控循环触发任务:

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

    至于如何实现这些类,我推荐一种方法,其中每个接收器都接受消息,然后将其卸载到自己的处理线程/任务中,以避免阻塞您的监控任务并阻止其他通知。

    【讨论】:

    • CallWebService(message) 执行后这个任务不会停止吗?在我的情况下,我不希望任务停止。它将永远运行。和thanx的链接。我会读的。
    • 是的,此任务之后将停止。我已经编辑了我的答案以提供一个无限运行的版本。
    • 非常感谢您的解释...我已经编辑了我的问题以包含一些关于我在做什么/如何做的代码 sn-ps...这将使您清楚地了解我需要什么.真的很抱歉之前没有提供这个......我应该有
    【解决方案2】:

    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 以设置为当前上下文。

    【讨论】:

    • 感谢您提供详细示例。如果我刚开始,我肯定会使用你的方法。但现在我投入了大量精力来编写不同的方法。我已经编辑了我的问题,以便为您提供我正在寻找的内容。我真的很抱歉之前没有提供这个......但是,在那种情况下,你不会告诉我 Progress......这看起来很酷......你能看看提供的代码再次并相应地建议我?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-23
    • 2019-03-12
    • 2019-10-24
    • 2012-11-02
    • 1970-01-01
    • 2016-08-02
    • 1970-01-01
    相关资源
    最近更新 更多