在另一个线程的线程中调用一个非线程安全的方法

时间:2017-11-21 21:33:07

标签: c# multithreading

假设我在主线程上有一个非线程安全的类X,并且我有另一个类Y在另一个线程中,需要调用类X的方法doX()。 我只是将类X的引用传递给类Y并从Y调用doX()但是这个类X是非线程安全的,如果从另一个线程调用则表现得很奇怪。

如何让X从X的线程调用X的方法doX()?在adminththid下面的SSCC中应始终保持相同(但不是)。

using System;
using System.Threading;

namespace ThreadApp
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            int managedThreadId = Thread.CurrentThread.ManagedThreadId;
            System.Diagnostics.Debug.WriteLine("Main ManagedThreadId = " + managedThreadId);

            X x = new X();
            x.doX();

            Y y = new Y();
            y.fun(x);
        }
    }

    class X
    {
        public void doX()
        {
            int managedThreadId = Thread.CurrentThread.ManagedThreadId;
            System.Diagnostics.Debug.WriteLine("X ManagedThreadId = " + managedThreadId);
        }
    }

    class Y
    {
        public void fun(X x)
        {
            Thread t = new Thread(x.doX);
            t.Start();
        }
    }
}

编辑:此页面比我更好地解释了我的问题:http://mikehadlow.blogspot.it/2012/11/using-blockingcollection-to-communicate.html

  

考虑这些(有些)常见的编程挑战:

     

我正在使用非线程安全的第三方库,但我想要我的   应用程序在多个线程之间共享工作。我如何编组   我的多线程代码之间调用单线程库?一世   在一个线程上有一个事件源,但我想分享   多线程池之间的工作?我有多个线程   发出事件,但我想在一个线程上使用它们?一   这样做的方法是拥有一些共享状态,一个字段或一个   静态类上的属性,并围绕它进行包装以使其成为多个   线程可以安全地访问它。这是一种非常常见的尝试方式   皮肤这只特别的猫,但它是用陷阱拍摄的   粗心。此外,它可能会损害性能,因为访问共享   资源是序列化的,即使访问它的东西是   并行运行。

     

更好的方法是使用BlockingCollection并拥有你的线程   通过消息类进行通信。

这是一个基于该网站建议使用BlockingCollection的工作解决方案:

namespace ThreadApp
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            int managedThreadId = Thread.CurrentThread.ManagedThreadId;
            System.Diagnostics.Debug.WriteLine("Main ManagedThreadId = " + managedThreadId);

            X x = new X();

            Y y = new Y();
            y.fun(x);

            x.doX();

        }
    }

    class X
    {
        private BlockingCollection<String> queue = new BlockingCollection<String>();

        public void Produce(String item)
        {
            queue.Add(item);
        }

        public void doX()
        {
            while (true)
            {
                String item = queue.Take();
                int managedThreadId = Thread.CurrentThread.ManagedThreadId;
                System.Diagnostics.Debug.WriteLine("X ManagedThreadId = " + managedThreadId + " randomid=" + item);
                // Add your code to process the item here.
                // Do not start another task or thread. 
            }
        }
    }

    class Y
    {
        X x;

        public void fun(X x)
        {
            this.x = x;
            Thread t = new Thread(threadBody);
            t.Start();
        }

        void threadBody()
        {
            while (true)
            {
                int managedThreadId = Thread.CurrentThread.ManagedThreadId;

                Random rand = new Random();
                int randInt = rand.Next(1, 90);
                System.Diagnostics.Debug.WriteLine("Y ManagedThreadId = " + managedThreadId + " random-int" + randInt);
                x.Produce("random-int" + randInt);
                Thread.Sleep(randInt * 10);
            }
        }
    }
}

以上解决方案有效,这是输出:

Main ManagedThreadId = 1
Y ManagedThreadId = 3 random-int24
X ManagedThreadId = 1 randomid=random-int24
Y ManagedThreadId = 3 random-int46
X ManagedThreadId = 1 randomid=random-int46
Y ManagedThreadId = 3 random-int48
X ManagedThreadId = 1 randomid=random-int48

Y线程插入一个random-int,X线程在队列中接收它,并在与主线程相同的线程中执行其方法。

然而问题是doX()方法在while循环中,所以它是阻塞的。如果我有一个X类,它有一些其他功能要做,并且无法阻止方法内的循环,这种方法不起作用......

1 个答案:

答案 0 :(得分:2)

这是一个很棒的方法。使用Microsoft的Reactive Framework(Rx)。

Rx主要提供一个非常强大的可观察/观察者模型,但它还提供了一组可用于简单地处理线程的调度程序。 EventLoopScheduler调度程序可用于确保代码在单个线程上运行。

试试这个例子:

var els = new System.Reactive.Concurrency.EventLoopScheduler();

Console.WriteLine("A" + Thread.CurrentThread.ManagedThreadId);

els.Schedule(() =>
{
    Console.WriteLine("B" + Thread.CurrentThread.ManagedThreadId);
});

var thread = new Thread((ThreadStart)(() =>
{
    Console.WriteLine("C" + Thread.CurrentThread.ManagedThreadId);
    els.Schedule(() =>
    {
        Console.WriteLine("D" + Thread.CurrentThread.ManagedThreadId);
    });
}));

thread.Start();

输出:

A12
B14
C16
D14

即使调度操作的调用来自两个不同的线程,"B""D"也在同一个线程上运行。

您可以使用EventLoopScheduler确保您在X上的代码在同一个帖子上运行。

Just NuGet&#34; System.Reactive&#34;得到这些。