跨线程编组事件

时间:2012-06-27 16:31:24

标签: c# .net multithreading marshalling

我想这可能会被标记为重复和封闭,但我不能为我的生活找到一个清晰,简洁的答案。所有回复和资源几乎都只使用Windows Forms并使用预构建的实用程序类(如BackgroundWorker)。我非常希望从理论上理解这个概念,因此我可以将基础知识应用于其他线程实现。

我想要实现的一个简单例子:

//timer running on a seperate thread and raising events at set intervals
//incomplete, but functional, except for the cross-thread event raising
class Timer
{
    //how often the Alarm event is raised
    float _alarmInterval;
    //stopwatch to keep time
    Stopwatch _stopwatch;
    //this Thread used to repeatedly check for events to raise
    Thread _timerThread;
    //used to pause the timer
    bool _paused;
    //used to determine Alarm event raises
    float _timeOfLastAlarm = 0;

    //this is the event I want to raise on the Main Thread
    public event EventHandler Alarm;

    //Constructor
    public Timer(float alarmInterval)
    {
        _alarmInterval = alarmInterval;
        _stopwatch = new Stopwatch();
        _timerThread = new Thread(new ThreadStart(Initiate));
    }

    //toggles the Timer
    //do I need to marshall this data back and forth as well? or is the
    //_paused boolean in a shared data pool that both threads can access?
    public void Pause()
    {
        _paused = (!_paused);            
    }

    //little Helper to start the Stopwatch and loop over the Main method
    void Initiate()
    {
        _stopwatch.Start();
        while (true) Main();    
    }

    //checks for Alarm events
    void Main()
    {
        if (_paused && _stopwatch.IsRunning) _stopwatch.Stop();
        if (!_paused && !_stopwatch.IsRunning) _stopwatch.Start();
        if (_stopwatch.Elapsed.TotalSeconds > _timeOfLastAlarm)
        {
            _timeOfLastAlarm = _stopwatch.Elapsed.Seconds;
            RaiseAlarm();
        }
    }
}

这里有两个问题;主要是,如何将事件发送到主线程以警告感兴趣的各方警报事件。

其次,关于Pause()方法,它将由主线程上运行的对象调用;我可以通过调用_stopwatch.start()/ _ stopwatch.stop()直接操作在后台线程上创建的秒表。如果没有,主线程是否可以如上图所示调整_paused布尔值,以便后台线程可以看到_paused的新值并使用它?

我发誓,我已经完成了我的研究,但这些(基本的和关键的)细节还没有让我清楚。

免责声明:我知道有些类可以提供我在Timer类中描述的确切特定功能。 (事实上​​,我相信这个类只是那个,Threading.Timer)但是,我的问题不是试图帮助我实现Timer类本身,而是理解如何执行驱动它的概念。

2 个答案:

答案 0 :(得分:1)

您无法在现有线程上神奇地执行代码 相反,您需要现有线程来显式执行您的代码,使用线程安全的数据结构来告诉它该做什么。

这就是Control.Invoke的工作原理(反过来BackgroundWorker如何运作) WiinForms在Application.Run()中运行一个消息循环,大致如下所示:

while(true) {
    var message = GetMessage();  //Windows API call
    ProcessMessage(message);
}

Control.Invoke()发送Windows消息(使用Windows中的线程安全消息传递代码)告诉它运行您的委托。 ProcessMessage(在UI线程上执行)将捕获该消息并执行委托。

如果你想自己做,你需要编写自己的循环。您可以在.Net 4.0中使用新的线程安全的Producer-Consumer集合,或者您可以使用委托字段(使用Interlocked.CompareExchange)和AutoResetEvent并自己完成。

答案 1 :(得分:1)

注意:我在这里写这篇文章是因为评论空间不够,这当然不是一个完整的,也不是一半完整的答案:

  

我总是使用事件来发送不相关的代码来做某事,所以这就是我描述我的意图的方式。请原谅我,我不确定我是否看到了编组和事件与编组其他类型数据(信号)之间的区别。

从概念上讲,两者都可以视为事件。使用提供的同步/信号对象和尝试通过自己实现这样的事情之间的区别在于谁以及如何完成工作。

.net中的事件只是一个委托,一个指向事件提供程序触发时应该执行的方法的指针列表。  您正在谈论的内容(编组事件),如果我理解正确的话,就会在事件发生时共享事件对象,而signalig的概念通常会讨论一个共享的对象来启动使用,并且两个线程通过手动或自动检查其状态来“知道”发生的事情(依赖于.net和windows提供的工具)。

在最基本的场景中,您可以使用boolean变量实现此类信令概念,其中一个线程不断循环以检查boolean的值是否为真,另一个设置为此类,如发出信号的方式。 .NET提供的不同信令工具在资源浪费较少的情况下做到这一点,只要没有信号(布尔值等于假),也不执行等待线程,但从概念上讲,它是相同的想法。 / p>