目标线程must“自身”运行代码 - 或者它只是跨线程访问对象。这是通过目标线程本身上某种形式的事件调度循环来完成的。
SynchronizationContext 抽象can and does支持这个if底层提供商支持它。例如,在 WinForms 或 WPF(它们本身使用“窗口消息泵”)中使用Post
将“在 UI 线程上运行”。
基本上,所有此类构造都遵循模式的一些变体:
// On "target thread"
while (running) {
var action = getNextDelegateFromQueue();
action();
}
// On other thread
postDelegateToQueue(actionToDoOnTargetThread);
创建一个相当简单原始手动队列系统 - 只需确保使用正确的同步防护即可。 (尽管我确信那里有整洁的“已解决的问题”库;包括将所有内容包装到 SynchronizationContext 中。)
Here is a primitive version of the manual queue. Note that there may be is1 a race condition.. but, FWIW:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace DogPark
{
internal class DogPark
{
private readonly string _parkName;
private readonly Thread _thread;
private readonly ConcurrentQueue<Action> _actions = new ConcurrentQueue<Action>();
private volatile bool _isOpen;
public DogPark(string parkName)
{
_parkName = parkName;
_isOpen = true;
_thread = new Thread(OpenPark);
_thread.Name = parkName;
_thread.Start();
}
// Runs in "target" thread
private void OpenPark(object obj)
{
while (true)
{
Action action;
if (_actions.TryDequeue(out action))
{
Program.WriteLine("Something is happening at {0}!", _parkName);
try
{
action();
}
catch (Exception ex)
{
Program.WriteLine("Bad dog did {0}!", ex.Message);
}
}
else
{
// Nothing left!
if (!_isOpen && _actions.IsEmpty)
{
return;
}
}
Thread.Sleep(0); // Don't toaster CPU
}
}
// Called from external thread
public void DoItInThePark(Action action)
{
if (_isOpen)
{
_actions.Enqueue(action);
}
}
// Called from external thread
public void ClosePark()
{
_isOpen = false;
Program.WriteLine("{0} is closing for the day!", _parkName);
// Block until queue empty.
while (!_actions.IsEmpty)
{
Program.WriteLine("Waiting for the dogs to finish at {0}, {1} actions left!", _parkName, _actions.Count);
Thread.Sleep(0); // Don't toaster CPU
}
Program.WriteLine("{0} is closed!", _parkName);
}
}
internal class Dog
{
private readonly string _name;
public Dog(string name)
{
_name = name;
}
public void Run()
{
Program.WriteLine("{0} is running at {1}!", _name, Thread.CurrentThread.Name);
}
public void Bark()
{
Program.WriteLine("{0} is barking at {1}!", _name, Thread.CurrentThread.Name);
}
}
internal class Program
{
// "Thread Safe WriteLine"
public static void WriteLine(params string[] arguments)
{
lock (Console.Out)
{
Console.Out.WriteLine(arguments);
}
}
private static void Main(string[] args)
{
Thread.CurrentThread.Name = "Home";
var yorkshire = new DogPark("Yorkshire");
var thunderpass = new DogPark("Thunder Pass");
var bill = new Dog("Bill the Terrier");
var rosemary = new Dog("Rosie");
bill.Run();
yorkshire.DoItInThePark(rosemary.Run);
yorkshire.DoItInThePark(rosemary.Bark);
thunderpass.DoItInThePark(bill.Bark);
yorkshire.DoItInThePark(rosemary.Run);
thunderpass.ClosePark();
yorkshire.ClosePark();
}
}
}
输出应如下所示 - 请记住,这将change由于非同步线程的固有性质而多次运行时。
Bill the Terrier is running at Home!
Something is happening at Thunder Pass!
Something is happening at Yorkshire!
Rosie is running at Yorkshire!
Bill the Terrier is barking at Thunder Pass!
Something is happening at Yorkshire!
Rosie is barking at Yorkshire!
Something is happening at Yorkshire!
Rosie is running at Yorkshire!
Thunder Pass is closing for the day!
Thunder Pass is closed!
Yorkshire is closing for the day!
Yorkshire is closed!
没有什么可以阻止一只狗同时在多个狗公园表演。
1 There is a race condition present and it is this: a park may close before the last dog action runs.
这是因为 Dog Park 线程在操作运行之前将该操作出列 - 并且关闭 Dog Park 的方法只会等到所有操作都出列。
有多种方法可以解决这个问题,例如:
- 并发队列可以先查看使用,然后在操作后出列,或者
- 可以使用单独的易失性 isClosed-for-real 标志(从狗公园线程设置),或者..
我已经离开了bug提醒人们穿线的危险。