我在C#应用程序中有一个静态的共享资源集合(文本处理器类),它可以响应关键字。每个处理器都是有状态的,因此在多线程上下文中不起作用。我正在寻找一种组织这些的好方法,以便我可以对它们进行线程安全访问。
例如,我可以在每个处理器中嵌入一个同步对象
public abstract class TextProcessor
{
protected internal readonly object sync = new object();
public abstract void Run( string text );
}
public class DocumentProcessor
{
private static Dictionary<string,TextProcessor> s_procMap = ...;
public void Run( string [] words1, string[] words2 )
{
ThreadPool.QueueUserWorkItem( Process, words1 );
ThreadPool.QueueUserWorkItem( Process, words2 );
}
private void Process( object arg )
{
foreach( var word in words )
{
var proc = s_processor[word];
lock( proc.sync )
{
proc.Run( word );
}
}
}
假设我无法将我的处理器重写为无状态(这是一种选择,但由于重构量而成为最后的选择),有没有更好的方法来表达它?
答案 0 :(得分:2)
当前方法的问题是单个忙TextProcessor
可以停止处理剩余的单词。如果您改为给每个TextProcessor
一个(线程安全的)单词队列,那么您可以在不阻塞的情况下将工作排队并让TextProcessor
出列并按顺序处理这些单词。
查看TPL Dataflow library的ActionBlock
。它处理排队,处理Task
的开始以及最终关闭Task
,以防再也没有工作。
请注意,当没有工作排队时,不会阻止任何线程。这是一个重要的可伸缩性属性。
答案 1 :(得分:1)
如果你真的不想改变处理器...那么我会做一个包装器。
public class ProcWrapper
{
private TextProcessor _proc;
private ActionBlock<string> _actBlock;
public ProcWrapper(TextProcessor proc)
{
_proc = proc;
_actBlock = new ActionBlock<string[]>(word=>
{
_proc.Run(word);
});
}
public void AddWord(string words)
{
_actBlock.Post(word);
}
public void WaitForCompletion()
{
_actBlock.Completion.Wait();
}
}
使用它与以前的方式类似:
Dictionary<string,ProcWrapper> s_procMap = ...;
void Run( string [] words )
{
// NOTE: This assumes the same thread will access s_procMap.
foreach(var word in words)
s_procMap[word].AddWord(word);
}
我认为这与你正在寻找的很接近。
请点击http://msdn.microsoft.com/en-us/library/hh228603.aspx了解DataFlow
TPL
和http://msdn.microsoft.com/en-us/library/hh194684.aspx的详细信息,了解ActionBlock<T>
的具体信息。