我有一个多线程程序,我想使用 Trace.WriteLine,并将输出重定向到文本文件:
_LogTracer = new System.Diagnostics.TextWriterTraceListener(logPath);
Trace.AutoFlush = true;
Trace.Listeners.Add(_LogTracer);
这足以保证线程安全吗?如果两个线程尝试同时写入输出文件怎么办?
{wrt。整体线程安全}
当明确禁用
Trace.Listeners.Add
时,相对于 Remove
(或 Trace.Write
),使用 WriteLine
(或 Trace.UseGlobalLock
)不是线程安全的。
Write
的线程安全(或缺乏)仅适用于单个跟踪侦听器类型(如前所述,TextWriterTraceListener 实现是线程安全的)或通过全局跟踪锁进行保护(有关讨论,请参阅TraceListener.IsThreadSafe
)。
我有一些糟糕的旧代码,它们修改了侦听器集合,这可能会导致多线程环境中出现错误:
[System.InvalidOperationException]
Collection was modified; enumeration operation may not execute.
-------------------------------------------------- -- >
at System.Collections.ArrayList.ArrayListEnumeratorSimple.MoveNext()
at System.Diagnostics.TraceInternal.WriteLine(String message, String category)
文档说
Listeners
是线程安全的,这有点误导:Listeners.GetEnumerator
确实 not 建立了暗示的锁定复制。线程安全锁只能通过几个单独的添加/删除方法获取。
要使
Trace.Write
相对于修改Listeners
而言是线程安全的,需要使用Trace.UseGlobalLock
,它将在枚举和写入所有侦听器时获取锁定。
来自 Microsoft 文档:
线程安全
该类型是线程安全的。
这段代码证明了
TextWriterTraceListener
是线程安全的。在 .net6 中测试
Trace.Listeners.Clear();
Trace.Listeners.Add(new TextWriterTraceListener("C:\\temp\\TEMP-\\mylog.log"));
var rand = new Random();
var sw = Stopwatch.StartNew();
var t1 = Task.Run(() => Write(sw, "Thread#1", rand));
var t2 = Task.Run(() => Write(sw, "Thread#2", rand));
var t3 = Task.Run(() => Write(sw, "Thread#3", rand));
Task.WaitAll(t1, t2, t3);
Trace.Close();
//-----------
static void Write(Stopwatch sw, string threadName, Random rand)
{
while(sw.Elapsed.TotalSeconds < 15)
{
int ms = rand.Next(10, 20);
System.Threading.Thread.Sleep(ms);
Trace.TraceInformation("Thread '{0}' @ {1}", threadName, ms);
}
}
还有文件
测试代码信息:0:线程“Thread#2”@14
测试代码信息:0:线程“Thread#1”@18
测试代码信息:0:线程“Thread#2”@15
测试代码信息:0:线程“Thread#1”@18
测试代码信息:0:线程“Thread#1”@13
测试代码信息:0:线程“Thread#2”@12
测试代码信息:0:线程“Thread#1”@12
测试代码信息:0:线程“Thread#2”@14
测试代码信息:0:线程“Thread#1”@12
测试代码信息:0:线程“Thread#1”@10
测试代码信息:0:线程“Thread#2”@11
测试代码信息:0:线程“Thread#1”@16
测试代码信息:0:线程“Thread#2”@16
测试代码信息:0:线程“Thread#1”@12
测试代码信息:0:线程“Thread#2”@14
测试代码信息:0:线程“Thread#1”@14
测试代码信息:0:线程“Thread#2”@18
测试代码信息:0:线程“Thread#1”@12
测试代码信息:0:线程“Thread#2”@12
测试代码信息:0:线程“Thread#2”@16
测试代码信息:0:线程“Thread#3”@18
测试代码信息:0:线程“Thread#1”@18
测试代码信息:0:线程“Thread#1”@11
测试代码信息:0:线程“Thread#1”@13
测试代码信息:0:线程“Thread#3”@19
测试代码信息:0:线程“Thread#2”@19
测试代码信息:0:线程“Thread#1”@10
测试代码信息:0:线程“Thread#3”@14
测试代码信息:0:线程“Thread#1”@10