在一个永久线程中运行事件处理程序

3

我已经寻找解决方案,但是没有发现任何有用的信息。

在某些类中,我有事件(event)。

public class ClassWithEvent
{
    public event Action<string> SomeEvent;

    ...
}

这个事件有订阅者

public class SubscriberClass
{
    public void SomeMethod(string value)
    {
        ...
    }
}

ClassWithEvent objectWithEvent = new ClassWithEvent();

SubscriberClass subscriberObject = new SubscriberClass();

objectWithEvent.SomeEvent += subscriberObject.SomeMethod;

在主线程中的某个位置可以调用此事件。

if(SomeEvent != null)
    SomeEvent(someString);

当它发生时,它的处理程序必须在第二个线程中运行。但每次都在同一个线程中,因此这个第二个线程必须是永久的,并且在第一次执行后不会被终止。

请帮我实现这个。

2个回答

7
创建一个共享队列:
private BlockingCollection<string> queue = new BlockingCollection<string>();

创建专用线程:
Thread thread = new Thread(HandleEvents);
thread.Start();

在该线程上处理事件:

private void HandleEvents()
{
    foreach (string someValue in queue.GetConsumingEnumerable()) 
    { 
        //Do stuff 
    }
}

当事件被触发时,将项目放入队列中:

objectWithEvent.SomeEvent += (x) => queue.Add(x);

在关机完成后,处理队列:

queue.CompleteAdding();

与其使用while循环,更简单的方法是使用foreach (string someValue in queue.GetConsumingEnumerable()) { //Do stuff }。它的行为与您的版本相同,除了在多个消费者环境中,Take()可能会在已经完成的集合上被调用而导致竞争情况。Take()会抛出异常,而GetConsumingEnumerable()则仅退出foreach循环。 - Scott Chamberlain
@ScottChamberlain 同意。 - Zer0

-1

这有点棘手,但应该能行。

您需要创建一个永久线程,例如通过在那里启动while(true),以及一个操作列表,在此循环内进行检查,类似于以下内容:

ConcurrentBag<Action> actions = new ConcurrentBag<Action>();
new Thread(
delegate()
{
    while (true)
    {
        Action a;
        if (actions.TryTake(out a))
           a();
    }
}
).Start();

每次想要触发事件时,只需将操作添加到列表中:

actions.Add(new Action(() => { SomeEvent("abc"); }));

好的,亲爱的downvoters(踩票者),我刚试图展示了一种方法,但现在这里给大家提供一个线程安全版本,其中包括添加到“Bag”中的代码。 - VladL
@VladL 如果袋子是空的,这段代码将会持续消耗 CPU 循环。如果袋子不为空,由于你从未从袋子中移除物品,你将会重复执行相同的“Action”。 - Zer0
@Zer0 TryPeek类似于Stack的Pop()。如果有一个项目,它将从包中删除该项目。 - VladL
@VladL 不,它不会。 - Zer0
@Zer0 真是太对了,我把它和 TryTake 搞混了。谢谢。 - VladL

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接