构建一个线程安全的程序的最佳方法是什么,它需要将双值写入文件。如果多个线程正在调用通过streamwriter保存值的函数?这是最好的方式吗?
评论代码:
static void Main()
{
List<double> Values = new List<double>();
StreamWriter writer = new StreamWriter("test.out");
for (int i = 0; i < 1000; i++) Values.Add(i);
foreach (double V in Values)
{
ThreadPool.QueueUserWorkItem(delegate(object state) { SaveValues(writer, V); }, V);
}
}
static void SaveValues(StreamWriter writer, double Value)
{
lock (writer) writer.WriteLine(Value);
}
答案 0 :(得分:7)
围绕指定的TextWriter创建一个线程安全的包装器。 ll写入返回的包装器将是线程安全的。
答案 1 :(得分:5)
你的问题是不锁定或线程,但捕获循环变量。这是一个经典问题,所有线程都使用单个V
变量的“捕获”运行,并且在执行线程时,它将达到其最终值999.除了可能的前几个工作项。
所以,而不是:
foreach (double V in Values)
{
ThreadPool.QueueUserWorkItem(delegate(object state)
{ SaveValues(writer, V); }, V); // use of captured V: almost always 999
}
使用
foreach (double V in Values)
{
double W = V;
ThreadPool.QueueUserWorkItem(delegate(object state)
{ SaveValues(writer, W); }, V);
}
或者,更简洁一点,
foreach (double V in Values)
{
double W = V;
ThreadPool.QueueUserWorkItem((state) => SaveValues(writer, W));
}
作为@Matti答案的变体,建议锁定一个单独的简单对象。这降低了锁定同一对象的任何其他东西的风险(例如,StreamWriter代码本身)。
private StreamWriter writer = ...;
private Object writerLock = new Object(); // don't expose through a property
// or any other way
lock (writerLock)
{
writer.Write(...);
}
但是你设置SaveValues(StreamWriter writer, ...)
方法的方式让它变得有点复杂。最好有一个writer
和writerLock
都是私有成员的对象。
答案 2 :(得分:3)
访问时锁定它。
lock (myStreamWriter) {
myStreamWriter.Write(...);
}
这解决了多线程尝试访问它的问题。当然,如果您需要进一步确保从多个线程以正确的顺序编写内容,则必须添加额外的逻辑。
答案 3 :(得分:3)
通常在进行输入或输出时,我更喜欢producer/consumer queue,只有一个消费者可以实际输出输出。
答案 4 :(得分:2)
您可以与StreamWriter上的锁(或线程想要访问StreamWriter时使用的同步对象)同步。但是,当多个线程尝试写入时,只能有一个线程继续写入。
如果这是一个问题,您可以改为实现一个队列,并添加一个编写器线程。编写器线程将出列,并写入StreamWriter。其他线程将对项目进行排队,而不是直接编写项目。
您必须具有某种形式的同步才能访问此队列。你可以在每个线程访问它时简单地锁定它。但是,如果你不小心,可能会产生比你不得不开始时更糟糕的性能问题。
修改强>
看起来Stephen Cleary有一个指向可用于实现队列的现有.NET类的链接。我会留下我的答案,因为它解释了为什么你想做生产者/消费者。