通过单个生产者线程获得数据的最有效方式是向多个消费者线程发出信号的最有效方法是什么?

时间:2014-05-22 16:24:58

标签: .net synchronization .net-2.0

我有一个数据源,它将定期为N个侦听器提供数据。

我可以使用a ReaderWriterLock来控制对数据的访问,但应该用什么来表示所有听众他们可以同时获取该数据的副本以满足他们自己的处理需求?

我最初认为an AutoResetEvent可能会这样做,但是从我所读过的内容中,只会发出一个单独的线程继续阅读?

编辑 - 我尝试了以下操作但主线程中没有Sleep没有触发任何内容。那是为什么?

// TriggeringMultipleThreads.cpp : main project file.

#include "stdafx.h"

using namespace System;
using namespace System::Threading;

static void Report(String^ caller, String^ message)
{
    DateTime^ now = DateTime::UtcNow;
    Console::WriteLine(String::Format("{0}: {1}: {2}", now->Ticks, caller, message));
}

static void ThreadProc(Object^ args)
{
    try
    {
        Collections::Generic::List<Object^>^ params = dynamic_cast<Collections::Generic::List<Object^>^>(args);
        String^ threadName = dynamic_cast<String^>(params[0]);
        int pauseBetweenLocks = static_cast<int>(params[1]);
        ReaderWriterLock^ masterLock = dynamic_cast<ReaderWriterLock^>(params[2]);
        EventWaitHandle^ newDataSignal = dynamic_cast<EventWaitHandle^>(params[3]);
        while (true)
        {
            Report(threadName, "waiting for new data signal");
            bool newDataAvailable = newDataSignal->WaitOne();
            if (newDataAvailable == true)
            {
                Report(threadName, "acquiring reader lock");
                masterLock->AcquireReaderLock(1000);
                Report(threadName, "got data lock");
                masterLock->ReleaseReaderLock();
                Report(threadName, "released data lock");
                if (pauseBetweenLocks > 0)
                {
                    Report(threadName, String::Format("sleeping for {0}ms", pauseBetweenLocks/2));
                    Thread::Sleep(pauseBetweenLocks/2);
                    Report(threadName, String::Format("sleeping for {0}ms", pauseBetweenLocks/2));
                    Thread::Sleep(pauseBetweenLocks/2);
                }
            }
            else
            {
                Report(threadName, String::Format("failed to find new data"));
            }
        }
    }
    catch(Exception^ e)
    {
        Report("Thread", String::Format("Thread died: {0}", e->Message));
    }
}

Thread^ CreateAndStartThread(String^ threadName, int delay, ReaderWriterLock^ dataLock, EventWaitHandle^ newDataSignal)
{
    Thread^ t1 = gcnew Thread(gcnew ParameterizedThreadStart(&ThreadProc));
    Collections::Generic::List<Object^>^ t1Params = gcnew Collections::Generic::List<Object^>(2);
    t1Params->Add(threadName);
    t1Params->Add(delay);
    t1Params->Add(dataLock);
    t1Params->Add(newDataSignal);
    t1->Start(t1Params);
    return t1;
}

int main(array<System::String ^> ^args)
{
    ReaderWriterLock^ masterLock = gcnew ReaderWriterLock();
    EventWaitHandle^ newDataSignal = gcnew ManualResetEvent(false);

    Thread^ t1 = CreateAndStartThread("Fast thread1",   0, masterLock, newDataSignal);
    Thread^ t2 = CreateAndStartThread("Slow thread", 100, masterLock, newDataSignal);
    Thread^ t3 = CreateAndStartThread("Fast thread2",   0, masterLock, newDataSignal);
    String^ masterThreadName = "Master";

    while (true)
    {
        Report(masterThreadName, "Ready");
        Console::ReadLine();

        Console::WriteLine();
        Report(masterThreadName, "Master thread acquiring lock");
        masterLock->AcquireWriterLock(1000);
        Report(masterThreadName, "signalling listeners");
        bool signalStarted = newDataSignal->Set();

        Thread::Sleep(1); // <---- Why is this required?

        bool signalReset = newDataSignal->Reset();
        Report(masterThreadName, String::Format("signalling listeners ({0}, {1})", signalStarted, signalReset));
        Report(masterThreadName, "releasing lock");
        masterLock->ReleaseWriterLock();
        Report(masterThreadName, "released lock");
    }

    return 0;
}

跑步给我(例如):

635364403105485943: Master: Ready
635364403105485943: Fast thread1: waiting for new data signal
635364403105485943: Fast thread2: waiting for new data signal
635364403105485943: Slow thread: waiting for new data signal

635364403132598654: Master: Master thread acquiring lock
635364403132598654: Master: signalling listeners
635364403132598654: Fast thread1: acquiring reader lock
635364403132598654: Slow thread: acquiring reader lock
635364403132608655: Master: signalling listeners (True, True)
635364403132608655: Master: releasing lock
635364403132618656: Master: released lock
635364403132618656: Fast thread1: got data lock
635364403132618656: Slow thread: got data lock
635364403132618656: Slow thread: released data lock
635364403132618656: Slow thread: sleeping for 50ms
635364403132618656: Master: Ready
635364403132598654: Fast thread2: acquiring reader lock
635364403132618656: Fast thread2: got data lock
635364403132628657: Fast thread2: released data lock
635364403132628657: Fast thread2: waiting for new data signal
635364403132618656: Fast thread1: released data lock
635364403132638658: Fast thread1: waiting for new data signal
635364403133118706: Slow thread: sleeping for 50ms
635364403133618756: Slow thread: waiting for new data signal

如果我在主线程中注释掉Thread::Sleep(1);,则其他线程都不会触发。

1 个答案:

答案 0 :(得分:2)

可以使用ManualResetEvent代替......但我会鼓励你使用更高级别的抽象。考虑您的任务是否适合Dataflow API - 或者可能为每个侦听器使用BlockingCollection,并在生产者创建一个项目时将其添加到每个集合中。 (你可能有一个&#34; tee-ing&#34;听众坐在制片人知道的单个BlockingCollection和听众之间。在那一点上,你真的重新发明了轮子。 ..)

哦,Reactive Extensions也可能适合你。

基本上,尽你所能避免像*ResetEventReaderWriterLock那样低级:)