[英]TPL Dataflow Blocks
問題:為什么使用WriteOnceBlock
(或BufferBlock
)從另一個BufferBlock<Action>
取回答案(如某種回調)(獲取答案在發布的Action
發生),會導致死鎖(在此代碼中)?
我認為類中的方法可以視為我們正在發送給對象的消息(例如,我認為-Alan Kay提出的有關OOP的原始觀點)。 因此,我編寫了這個通用的Actor
類,該類有助於將普通對象轉換為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();
}
此處用於創建Task
的不同TaskCreationOptions
。 也許我對這里的TPL Dataflow概念是錯誤的,才剛剛開始使用它(一個[ThreadStatic]
隱藏在某個地方?)。
您的代碼有問題的部分是: 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);
增加線程池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());
});
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.