处理来自单个线程上的多个线程的请求 - .NET Core

时间:2017-02-22 16:37:27

标签: c# multithreading .net-core

道歉,如果这个问题已经有答案,但如果有,我在这个网站上找不到。

首先 - 此问题特定于.NET核心(撰写本文时为v1.1.0)

我有一个第三方程序集,它只处理对它发出的请求,如果它们来自同一个线程。这个程序集是RabbitMQ库,问题的详细信息可能相关或不相关can be found here。基本上 - 我有多个线程可能会调用此程序集 - 但如果请求来自不同的线程 - 它会引发异常。

所以 - 为了解决这个问题,我试图创建一个被阻塞的线程,因此不会过期 - 并且必须以某种方式在此线程上处理对此程序集的所有调用。

我的第一次尝试是创建一个事件并在被阻止的线程上订阅该事件。然后任何其他线程将开始调用此事件,我认为可以在正确的线程上接收,以便我可以实现我在单线程上处理第三方程序集请求的愿望。

我现在(痛苦地)理解it is not possible in .NET core to begin-invoke an event in .Net core :(

演示问题的示例:

public class Program
{
    public static void Main(string[] args)
    {
        Program program = new Program();
        ManualResetEvent resetEvent = new ManualResetEvent(false);
        program.StartNewThreadToBeCalled(resetEvent);

        program.CallBobMultipleTimes(resetEvent);

        Console.ReadLine();
    }

    private void CallBobMultipleTimes(ManualResetEvent resetEvent)
    {
         resetEvent.WaitOne();
         for(int i=0 ; i<100 ; i++)
            ThreadPool.QueueUserWorkItem(x=>CallBob(null, null)); //Can't BeginInvoke in .NET Core
    }

    private void StartNewThreadToBeCalled(ManualResetEvent resetEvent)
    {
        ThreadPool.QueueUserWorkItem(x=>
        {   
            Bob bob = new Bob();

            CallBob += (obj, e)=> bob.InvokeMeOnOneThreadOnly();
            resetEvent.Set();                
            ManualResetEvent mre = new ManualResetEvent(false);
            mre.WaitOne();  //Real implementation will block forever - this should be the only thread handles the processing of requests.
        });
    }

    public event EventHandler CallBob;
}

public class Bob
{
    private List<int> ThreadIdentifiers = new List<int>();
    private static object SyncObject = new object();


    public void InvokeMeOnOneThreadOnly()
    {   
        lock(SyncObject)
        {
            int currentThreadId = Thread.CurrentThread.ManagedThreadId;
            if(ThreadIdentifiers.Any())
            {
                if(!ThreadIdentifiers.Contains(currentThreadId))
                   Console.WriteLine("Don't call me from multiple threads!");
            }
            else
                ThreadIdentifiers.Add(currentThreadId);
        }
    }
}

我通过在concurrentQueue周围创建一个包装器来实现这一点,该包装器会在添加任何内容时通知它。然后,我在我的特殊第三方程序集线程上处理此事件,并从此队列中选择请求,直到它耗尽...不确定为什么这样做时,另一种方式不会?!

在.NET核心的单个线程上处理来自多个不同线程的多个请求有没有比我发现的更好的方法?

2 个答案:

答案 0 :(得分:2)

很难说出你在代码中尝试做什么。但是,另一种方法是使用left: -1000%; right: 1000%;。后台线程可以向该集合添加值,并且单个线程可以坐下并尝试从集合中获取值,例如:

BlockingCollection<T>

后台线程可以向阻塞集合添加新值:

while(continueProcessingCollection)
{
    if(blockingCollection.TryTake(out value, TimeSpan.FromSeconds(myTimeout)))
    {
        // process value, decide to toggle continueProcessingCollection
    }
}

而且,在某处你需要创建blockingCollection变量:

blockingCollection.Add(newValue);

答案 1 :(得分:1)

delegate BeginInvoke使用threadpool来安排无法解决问题的callback。这基本上是多个生产者和一个消费者的Producer–consumer问题。 .NET核心包括some structures实现 您可以使用IProducerConsumerCollection<T>来解决此问题。

不要与Control.BeginInvoke(winform)和Dispatcher.BeginInvoke(WPF)混淆,后者会在GUI线程上对回调进行排队。