【问题标题】:Program is Threadsafe or not c# [duplicate]程序是否是线程安全的c# [重复]
【发布时间】:2015-11-13 21:19:36
【问题描述】:

我的程序中有一个方法,需要知道它是否是线程安全的。我收到 System.I.O 异常写入这个文件,虽然它不是很一致,它会终止我的程序

方法外提到了文件位置。

private readonly string fileNameAndPath = @"C:\Apps\TestService\log\Test.txt";

public void LogRequestDetails_SendOrderRequestMethod(Message request, SendOrderRequest quoteRequest)
{
    // Create a writer and open the file:
    StreamWriter log;

    if (!File.Exists(fileNameAndPath))
           log = new StreamWriter(fileNameAndPath);
    else
           log = File.AppendText(fileNameAndPath);

    // Write to the file:
    log.WriteLine("=================================");
    log.WriteLine(DateTime.Now);
    log.WriteLine("Price : " + quoteRequest.Price);
    log.WriteLine("QuoteId : " + quoteRequest.QuoteId);
    log.WriteLine("SecurityId : " + quoteRequest.SecurityId);
    log.WriteLine();
    log.WriteLine("Request string : " + request.ToString());
    log.WriteLine("=================================");
    log.WriteLine("\n");

    // Close the stream:
    log.Close();
}

请查看下面的堆栈跟踪

Application: BestInvest.Select.ShareDealingService.exe

Framework Version: v4.0.30319

Description: The process was terminated due to an unhandled exception.

Exception Info: System.IO.IOException

Stack:



Server stack trace: 

   at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)

   at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)

   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)

   at System.IO.StreamWriter.CreateFile(String path, Boolean append, Boolean checkHost)

   at System.IO.StreamWriter..ctor(String path, Boolean append, Encoding encoding, Int32 bufferSize, Boolean checkHost)

   at System.IO.StreamWriter..ctor(String path, Boolean append)

   at System.IO.File.AppendText(String path)

   at BestInvest.Direct.Integration.FIXService.ShareDealingEngine.LogRequestDetails_SendOrderRequestMethod(Message request, SendOrderRequest quoteRequest)

   at BestInvest.Direct.Integration.FIXService.ShareDealingEngine.<>c__DisplayClass5.<SendOrder>b__2()

   at System.Runtime.Remoting.Messaging.StackBuilderSink._PrivateProcessMessage(IntPtr md, Object[] args, Object server, Object[]& outArgs)

   at System.Runtime.Remoting.Messaging.StackBuilderSink.AsyncProcessMessage(IMessage msg, IMessageSink replySink)

   at System.Runtime.Remoting.Proxies.RealProxy.EndInvokeHelper(System.Runtime.Remoting.Messaging.Message, Boolean)

   at System.Runtime.Remoting.Proxies.RemotingProxy.Invoke(System.Object, System.Runtime.Remoting.Proxies.MessageData ByRef)

   at System.Action.EndInvoke(System.IAsyncResult)

   at System.Runtime.Remoting.Messaging.AsyncResult.SyncProcessMessage(System.Runtime.Remoting.Messaging.IMessage)

   at System.Runtime.Remoting.Messaging.StackBuilderSink.AsyncProcessMessage(System.Runtime.Remoting.Messaging.IMessage, System.Runtime.Remoting.Messaging.IMessageSink)

   at System.Runtime.Remoting.Proxies.AgileAsyncWorkerItem.ThreadPoolCallBack(System.Object)

   at System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(System.Object)

   at System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)

   at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)

   at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()

   at System.Threading.ThreadPoolWorkQueue.Dispatch()

   at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()

【问题讨论】:

  • 如果两个线程同时调用该方法怎么办?在这种情况下,您可能会弄乱两条日志记录(例如,第一个线程写 3 行,然后第二个线程写 2 行,然后是第一个线程)

标签: c#


【解决方案1】:

它不是线程安全的。您可以通过使用锁来保护代码的关键部分免受竞争条件的影响。

private readonly string fileNameAndPath = @"C:\Apps\TestService\log\Test.txt";

private Object thisLock = new Object();

public void LogRequestDetails_SendOrderRequestMethod(Message request, SendOrderRequest quoteRequest)
{
  lock (thisLock)
  {
     // Create a writer and open the file:
     StreamWriter log;

     if (!File.Exists(fileNameAndPath))
       log = new StreamWriter(fileNameAndPath);
     else
       log = File.AppendText(fileNameAndPath);

     // Write to the file:
     log.WriteLine("=================================");
     log.WriteLine(DateTime.Now);
     log.WriteLine("Price : " + quoteRequest.Price);
     log.WriteLine("QuoteId : " + quoteRequest.QuoteId);
     log.WriteLine("SecurityId : " + quoteRequest.SecurityId);
     log.WriteLine();
     log.WriteLine("Request string : " + request.ToString());
     log.WriteLine("=================================");
     log.WriteLine("\n");

     // Close the stream:
     log.Close();
 }
}

更多详情请参阅参考资料: https://msdn.microsoft.com/en-us/library/a8544e2s.aspx

【讨论】:

    【解决方案2】:

    我认为File.ExistsFile.AppendText 是线程安全的,因为它们是静态调用。

    但是,对StreamWriter.WriteLine 的实例方法调用不会,所以我认为一个线程可能正在写入文件,而另一个线程可能会在第一个线程完成之前尝试追加,这可能会导致异常。

    或者实际上两个线程可以尝试同时使用StreamWriter 写入文件。

    System.IOException 上的确切消息是什么?

    【讨论】:

    • 我已经在上面添加了堆栈跟踪
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-08-21
    • 2018-03-29
    • 1970-01-01
    • 2019-11-21
    • 2013-12-30
    • 1970-01-01
    相关资源
    最近更新 更多