同步线程 - 没有UI

时间:2010-03-17 15:16:47

标签: c# multithreading synchronization

我正在尝试编写多线程代码并面临一些同步问题。我知道这里有很多帖子,但我找不到合适的东西。

我有一个System.Timers.Timer,它每隔30秒就会进入数据库并检查是否有新的工作。如果他找到了一个,他就会在当前线程上执行该作业(计时器为每个经过的时间打开新线程)。当作业正在运行时,我需要通知主线程(计时器所在的位置)有关进度的信息。

注意:

  1. 我没有UI,所以我不能像通常在winforms中那样beginInvoke(或使用后台线程)。
  2. 我想在我的主课上实现ISynchronizeInvoke,但这看起来有点矫枉过正(也许我在这里错了)。
  3. 我的工作类中有一个事件,主要类注册到它,我会在需要时调用该事件,但我担心它可能会导致阻塞。
  4. 每项工作最多可能需要20分钟。
  5. 我最多可以同时运行20个作业。
  6. 我的问题是:

    通知我的主线程有关我的工作线程中的任何进展的正确方法是什么?

    感谢您的帮助。

4 个答案:

答案 0 :(得分:2)

您还可以使用lock来实现一个线程安全的JobManager类,该类跟踪有关不同工作线程的进度。在这个例子中,我只维护活动工作线程计数,但这可以扩展到您的进度报告需求。

class JobManager
{
    private object synchObject = new object();

    private int _ActiveJobCount;

    public int ActiveJobsCount
    {
        get { lock (this.synchObject) { return _ActiveJobCount; } }
        set { lock (this.synchObject) { _ActiveJobCount = value; } }
    }

    public void Start(Action job)
    {
        var timer = new System.Timers.Timer(1000);

        timer.Elapsed += (sender, e) =>
        {
            this.ActiveJobsCount++;
            job();
            this.ActiveJobsCount--;
        };

        timer.Start();
    }
}

示例:

class Program
{
    public static void Main(string[] args)
    {
        var manager = new JobManager();

        manager.Start(() => Thread.Sleep(3500));

        while (true)
        {
            Console.WriteLine(manager.ActiveJobsCount);

            Thread.Sleep(250);
        }
    }
}

答案 1 :(得分:1)

使用活动。例如,BackgroundWorker类专门针对您的想法而设计。

http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx

ReportProgress函数以及ProgressChanged事件将用于进度更新。

pullJobTimer.Elapsed += (sender,e) =>
{
    BackgroundWorker worker = new BackgroundWorker();
    worker.WorkerReportsProgress = true;
    worker.DoWork += (s,e) =>
    {
        // Whatever tasks you want to do
        // worker.ReportProgress(percentComplete);
    };
    worker.ProgressChanged += mainThread.ProgressChangedEventHandler;
    worker.RunWorkerAsync();
};

答案 2 :(得分:1)

您可以通过回调方法通知主线程的进度。那就是:

// in the main thread
public void ProgressCallback(int jobNumber, int status)
{
    // handle notification
}

您可以在调用它时将该回调方法传递给工作线程(即作为委托),或者工作线程的代码可以隐式“知道”它。无论哪种方式都有效。

jobNumber和status参数只是示例。您可能希望使用其他方法来标识正在运行的作业,并且您可能希望使用枚举类型作为状态。无论如何,请注意ProgressCallback将由多个线程同时调用,因此如果您要更新任何共享数据结构或编写日志记录信息,则必须使用锁或其他同步技术保护这些资源。

您也可以使用事件,但保持主线程的事件订阅是最新的可能是一个潜在的问题。如果您忘记取消订阅特定工作线程事件中的主线程,您还可能会发生内存泄漏。虽然事件肯定会有用,但我会建议这个应用程序的回调。

答案 3 :(得分:1)

如果您不介意依赖.NET 3.0,可以使用Dispatcher来编组线程之间的请求。它的行为方式与Windows窗体中的Control.Invoke()类似,但没有Forms依赖项。您需要添加对WindowsBase程序集的引用(.NET 3.0及更新版本的一部分,并且是WPF的基础)

如果您不能依赖于.NET 3.0,那么我会说您从一开始就使用了正确的解决方案:在主类中实现ISynchronizeInvoke接口并传递到Timer的SynchronizingObject属性。然后将在主线程上调用您的计时器回调,然后可以生成BackgroundWorkers来检查数据库并运行任何排队的作业。作业将通过ProgressChanged事件报告进度,该事件将自动封送对主线程的调用。

快速谷歌搜索显示this example如何实际实现ISynchronizeInvoke界面。