假设我在主线程上有一个非线程安全的类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类,它有一些其他功能要做,并且无法阻止方法内的循环,这种方法不起作用......
答案 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;得到这些。