从Timer调用异步方法时,Monitor.Exit上的C#SynchronizationLockException

时间:2014-01-26 20:41:15

标签: asynchronous synchronization locking monitor async-await

我为客户端(Windows Phone 8)提供了以下3种简化方法 - 我正在使用的服务器C#app:

 private object sockLock = 1;
 private System.Threading.Timer _timer = new Timer(onTimer);

 void _sock_MessageReceived( ... )
 {
     lock(sockLock)
     {
         if( something )  //the server received the packet, so i can stop the timer
            _timer.Change(Timeout.Infinite, Timeout.Infinite);
     }
 }

 void onTimer( ... )
 {
    bool sendRequst = false;
    lock(sockLock)
    {
        if ( conditions from critical section )
           sendRequest = true;
    }
    if(sendRequest)
         SendRequest( ... ); //replay the packet
 }

 async void SendRequest(Request req)  //also called from GUI or user input
 {
        byte[] data = req.ToByteArray();
        if (data.Length == 0)
            return;

        bool lockTaken = false;
        Monitor.Enter(sockLock, ref lockTaken);
        if(!lockTaken)
        {
            Debug.WriteLine("Monitor.Enter failed !!!");
            return;
        }
        try
        {
            DataWriter dw = await GetSockStreamAsync();   //doesn't use lock in any way, it's just a lazy init

            lastReq = req;
            dw.WriteBytes(data);
            await dw.StoreAsync();

            await dw.FlushAsync();
            _timer.Change(10, Timeout.Infinite);
        }
        finally
        {
            Monitor.Exit(sockLock);  //here it throws exception
        }
 }

长话短说:我正在向服务器发送一些UDP数据包,服务器也会响应它收到的最后一个SEQ值。发送数据包后,我启动一个计时器,如果服务器没有及时响应,我重播该数据包。

在除SendRequest方法之外的所有方法中,我都可以使用锁,因此无法忘记释放sockLock。

然而,在SendRequest中,我需要'等待',因此我无法使用锁定,因为我没有多想,所以同步的解决方案是使用Monitor。

现在,当从GUI调用SendRequest时,它可以工作,但是当它从onTimer函数调用时,它会在Monitor.Exit(sockLock)处抛出SynchronizationLockException。

     Object synchronization method was called from an unsynchronized block of code.
   at System.Threading.Monitor.Exit(Object obj)
   at AbsMouse.AbsMouseClient.<SendRequestAsync>d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.AsyncMethodBuilderCore.<ThrowAsync>b__1(Object state)
   at System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(Object state)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
   at System.Threading.ThreadPoolWorkQueue.Dispatch()
   at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()}

“等待”是否会导致任何隐藏的副作用,或者我在此问题上缺少的其他任何内容?

提前致谢!

0 个答案:

没有答案