问题:为什么使用WriteOnceBlock
(或BufferBlock
)从另一个BufferBlock<Action>
获取答案(如回调)(回复答案发生在Action
Actor
1}})导致死锁(在此代码中)?
我认为类中的方法可以被视为我们发送给对象的消息(就像我认为--Alan Kay所提出的关于OOP的原始观点)。所以我编写了这个通用的Actor
类,它有助于将普通对象转换为public class Actor<T>
{
private readonly T _processor;
private readonly BufferBlock<Action<T>> _messageBox = new BufferBlock<Action<T>>();
public Actor(T processor)
{
_processor = processor;
Run();
}
public event Action<T> Send
{
add { _messageBox.Post(value); }
remove { }
}
private async void Run()
{
while (true)
{
var action = await _messageBox.ReceiveAsync();
action(_processor);
}
}
}
public interface IIdGenerator
{
long Next();
}
(当然,由于可变性和事物,这里有许多看不见的漏洞,但这不是主要的关注点)
所以我们有这些定义:
static void Main(string[] args)
{
var idGenerator1 = new IdInt64();
var idServer1 = new Actor<IIdGenerator>(idGenerator1);
const int n = 1000;
for (var i = 0; i < n; i++)
{
var t = new Task(() =>
{
var answer = new WriteOnceBlock<long>(null);
Action<IIdGenerator> action = x =>
{
var buffer = x.Next();
answer.Post(buffer);
};
idServer1.Send += action;
Trace.WriteLine(answer.Receive());
}, TaskCreationOptions.LongRunning); // Runs on a separate new thread
t.Start();
}
Console.WriteLine("press any key you like! :)");
Console.ReadKey();
Trace.Flush();
}
现在;为什么这段代码有效:
static void Main(string[] args)
{
var idGenerator1 = new IdInt64();
var idServer1 = new Actor<IIdGenerator>(idGenerator1);
const int n = 1000;
for (var i = 0; i < n; i++)
{
var t = new Task(() =>
{
var answer = new WriteOnceBlock<long>(null);
Action<IIdGenerator> action = x =>
{
var buffer = x.Next();
answer.Post(buffer);
};
idServer1.Send += action;
Trace.WriteLine(answer.Receive());
}, TaskCreationOptions.PreferFairness); // Runs and is managed by Task Scheduler
t.Start();
}
Console.WriteLine("press any key you like! :)");
Console.ReadKey();
Trace.Flush();
}
此代码不起作用:
TaskCreationOptions
此处使用不同的Task
来创建[ThreadStatic]
。也许我在这里对TPL Dataflow概念错了,刚开始使用它(A {{1}}隐藏在哪里?)。
答案 0 :(得分:3)
您的代码存在的问题是这部分:answer.Receive()
。
当你在动作中移动它时,僵局不会发生:
var t = new Task(() =>
{
var answer = new WriteOnceBlock<long>(null);
Action<IIdGenerator> action = x =>
{
var buffer = x.Next();
answer.Post(buffer);
Trace.WriteLine(answer.Receive());
};
idServer1.Send += action;
});
t.Start();
那为什么呢? answer.Receive();
,而不是await answer.ReceiveAsnyc();
阻止线程,直到返回答案。当你使用TaskCreationOptions.LongRunning
时,每个任务都有自己的线程,所以没有问题,但没有它(TaskCreationOptions.PreferFairness
无关紧要)所有线程池线程都忙着等待所以一切都很多慢点。它实际上并没有死锁,你可以看到当你使用15而不是1000时。
还有其他解决方案可以帮助您理解问题:
ThreadPool.SetMinThreads(1000, 0);
增加线程池。ReceiveAsnyc
:
Task.Run(async () =>
{
var answer = new WriteOnceBlock<long>(null);
Action<IIdGenerator> action = x =>
{
var buffer = x.Next();
answer.Post(buffer);
};
idServer1.Send += action;
Trace.WriteLine(await answer.ReceiveAsync());
});