2012-01-26 35 views
17

Tôi đã tìm thấy các bài viết khác nhau về ngoại lệ này nhưng không có trường hợp nào trong số đó là trường hợp của tôi. Đây là mã nguồn:Phương thức đồng bộ hóa đối tượng được gọi từ một khối mã không đồng bộ. Ngoại lệ trên Mutex.Release()

class Program 
{ 

    private static Mutex mutex; 
    private static bool mutexIsLocked = false; 
    static void Main(string[] args) 
    { 

     ICrmService crmService = 
      new ArmenianSoftware.Crm.Common.CrmServiceWrapper(GetCrmService("Armsoft", "crmserver")); 
     //Lock mutex for concurrent access to workflow 
     mutex = new Mutex(true, "ArmenianSoftware.Crm.Common.FilterCtiCallLogActivity"); 
     mutexIsLocked = true; 

     //Create object for updating filtered cti call log 
     ArmenianSoftware.Crm.Common.FilterCtiCallLog filterCtiCallLog = 
      new ArmenianSoftware.Crm.Common.FilterCtiCallLog(crmService); 
     //Bind events 
     filterCtiCallLog.CtiCallsRetrieved += new EventHandler<ArmenianSoftware.Crm.Common.CtiCallsRetrievedEventArgs>(filterCtiCallLog_CtiCallsRetrieved); 

     //Execute filter 
     try 
     { 
      filterCtiCallLog.CreateFilteredCtiCallLogSync(); 
     } 
     catch (Exception ex) 
     { 
      throw ex; 
     } 
     finally 
     { 
      if (mutexIsLocked) 
      { 
       mutexIsLocked = false; 
       mutex.ReleaseMutex(); 
      } 
     } 
    } 

    static void filterCtiCallLog_CtiCallsRetrieved(object sender, 
     ArmenianSoftware.Crm.Common.CtiCallsRetrievedEventArgs e) 
    { 
     tryasasas 
     { 
      if (mutexIsLocked) 
      { 
       mutexIsLocked = false; 
       mutex.ReleaseMutex(); 
      } 
     } 
     catch (Exception ex) 
     { 
      throw ex; 
     } 
    } 
} 

filterCtiCallLog.CreateFilteredCtiCallLogSync(); chức năng thực hiện các yêu cầu tới máy chủ, và làm tăng một số sự kiện, một trong số đó là CtiCallsRetrieve sự kiện. Và tôi cần phải phát hành mutex khi sự kiện này được kích hoạt. Nhưng khi gọi hàm ngoại lệ mutex.Release() được ném. CreateFilteredCtiCallLogSync hoạt động đồng bộ. Vấn đề là gì?

Trả lời

33

Giữ một bool xung quanh cho biết rằng mutex được sở hữu là một sai lầm nghiêm trọng. Bạn không làm cho bool thread-safe. Bạn đã nhận được vào dưa chua này vì bạn đang sử dụng đối tượng đồng bộ hóa sai. Một mutex có ái lực chủ đề, chủ sở hữu của một mutex là một chủ đề. Các chủ đề mà mua nó cũng phải là một trong những cuộc gọi ReleaseMutex(). Đó là lý do tại sao mã của bạn bom.

Bạn có thể cần một sự kiện tại đây, sử dụng AutoResetEvent. Tạo nó trong chủ đề chính, gọi Set() trong công nhân, WaitOne() trong luồng chính để đợi nhân viên hoàn thành công việc của mình. Và vứt bỏ nó sau đó. Cũng lưu ý rằng bằng cách sử dụng một thread để thực hiện một công việc và có chủ đề chính của bạn chờ đợi cho hoàn thành của nó là không hiệu quả. Bạn cũng có thể có chủ đề chính thực hiện công việc.

Nếu bạn đang thực sự thực hiện điều này để bảo vệ quyền truy cập vào đối tượng không an toàn cho chủ đề (không rõ ràng), hãy sử dụng câu lệnh khóa.

+0

Bạn nói đúng - Tôi đã bỏ lỡ rằng việc thiết lập chuỗi gọi lại là chủ đề chính: ( –

+0

AutoResetEvent thật tuyệt vời! Cảm ơn Hans. –

2

Tôi chỉ có điều này một lần hoặc hai lần, và trong mọi trường hợp nó đến bằng cách cố gắng để phát hành một mutex tôi không sở hữu.

Bạn có chắc chắn các sự kiện được nêu lên trên cùng một chuỗi mà mutex đã được mua không? Mặc dù bạn đề cập rằng filterCtiCallLog.CreateFilteredCtiCallLogSync() là một cuộc gọi chặn, có lẽ nó sinh ra các chuỗi công nhân để tăng sự kiện?

2

Sử dụng cờ để cố gắng theo dõi trạng thái đối tượng đồng bộ hạt nhân sẽ không hoạt động - điểm sử dụng các cuộc gọi đồng bộ đó là chúng hoạt động chính xác mà không cần kiểm tra rõ ràng. Đặt cờ sẽ chỉ gây ra các sự cố gián đoạn vì cờ có thể được thay đổi không thích hợp do gián đoạn giữa việc kiểm tra cờ và hành động trên cờ.

Một mutex chỉ có thể được giải phóng bằng mối đe dọa có được nó. Nếu bạn gọi lại được gọi bằng một luồng khác, (một bên trong CreateFilteredCtiCallLogSync() hoặc một nhóm luồng hạt nhân), bản phát hành sẽ thất bại.

Không rõ chính xác những gì bạn đang cố gắng làm. Có lẽ, bạn muốn serialize quyền truy cập vào CreateFilteredCtiCallLogSync() và các cờ gọi lại rằng cá thể có sẵn để tái sử dụng? Nếu vậy, bạn có thể sử dụng một semaphore thay thế - init. nó đến một đơn vị, chờ cho nó ở đầu và phát hành nó trong gọi lại.

Có vấn đề nào đôi khi gọi lại không được gọi, và do đó thử/cuối cùng/phát hành? Nếu như vậy theo cách này có vẻ hơi tinh ranh nếu cuộc gọi lại không đồng bộ và có thể được gọi bởi một luồng khác sau khi chuỗi thiết lập đã rời khỏi hàm.

5

Tôi đã tìm thấy sự cố. Đầu tiên là vài thứ về lớp filterCtiCallLog. Tôi đã thiết kế nó để làm việc cả không đồng bộ và đồng bộ. Đầu tiên tôi đã viết mã để thực thi không đồng bộ. Tôi cần một cách để kích hoạt các sự kiện từ chuỗi công nhân con cho cha mẹ, để báo cáo trạng thái làm việc. Đối với điều này tôi đã sử dụng lớp học AsyncOperation và đó là phương pháp bài. Đây là phần mã để kích hoạt sự kiện CtiCallsRetrieved.

public class FilterCtiCallLog 
{ 
    private int RequestCount = 0; 
    private AsyncOperation createCallsAsync = null; 
    private SendOrPostCallback ctiCallsRetrievedPost; 
    public void CreateFilteredCtiCallLogSync() 
    { 
     createCallsAsync = AsyncOperationManager.CreateOperation(null); 
     ctiCallsRetrievedPost = new SendOrPostCallback(CtiCallsRetrievedPost); 
     CreateFilteredCtiCallLog(); 
    } 

    private void CreateFilteredCtiCallLog() 
    { 
     int count=0; 
     //do the job 
     //............ 
     //........... 
     //Raise the event 
     createCallsAsync.Post(CtiCallsRetrievedPost, new CtiCallsRetrievedEventArgs(count)); 
     //........... 
     //........... 
    } 

    public event EventHandler<CtiCallsRetrievedEventArgs> CtiCallsRetrieved; 

    private void CtiCallsRetrievedPost(object state) 
    { 
     CtiCallsRetrievedEventArgs args = state as CtiCallsRetrievedEventArgs; 
     if (CtiCallsRetrieved != null) 
      CtiCallsRetrieved(this, args); 
    } 
} 

Như bạn có thể thấy mã đang thực thi đồng bộ. Vấn đề ở đây là phương pháp AsyncOperation.Post(). Tôi cho rằng nếu nó được gọi trong chủ đề chính nó sẽ hoạt động đơn giản là kích hoạt sự kiện, chứ không phải đăng nó lên chủ đề chính. Tuy nhiên không phải vậy. Tôi không biết nó hoạt động như thế nào, nhưng tôi đã thay đổi mã, để kiểm tra xem CreateFilteredCtiCallLog được gọi là đồng bộ hay không đồng bộ. Và nếu nó là async gọi tôi sử dụng phương pháp AsyncOperation.Post, nếu không, tôi đã chỉ cần kích hoạt các EventHandler nếu nó không phải là null. Đây là mã được sửa chữa

public class FilterCtiCallLog 
{ 
    private int RequestCount = 0; 
    private AsyncOperation createCallsAsync = null; 
    private SendOrPostCallback ctiCallsRetrievedPost; 
    public void CreateFilteredCtiCallLogSync() 
    { 
     createCallsAsync = AsyncOperationManager.CreateOperation(null); 
     ctiCallsRetrievedPost = new SendOrPostCallback(CtiCallsRetrievedPost); 
     CreateFilteredCtiCallLog(false); 
    } 

    private void CreateFilteredCtiCallLog(bool isAsync) 
    { 
     int count=0; 
     //do the job 
     //............ 
     //........... 
     //Raise the event 
     RaiseEvent(CtiCallsRetrievedPost, new CtiCallsRetrievedEventArgs(count),isAsync); 
     //........... 
     //........... 
    } 

    public event EventHandler<CtiCallsRetrievedEventArgs> CtiCallsRetrieved; 

    private void RaiseEvent(SendOrPostCallback callback, object state, bool isAsync) 
    { 
     if (isAsync) 
      createCallsAsync.Post(callback, state); 
     else 
      callback(state); 
    } 

    private void CtiCallsRetrievedPost(object state) 
    { 
     CtiCallsRetrievedEventArgs args = state as CtiCallsRetrievedEventArgs; 
     if (CtiCallsRetrieved != null) 
      CtiCallsRetrieved(this, args); 
    } 
} 

Cảm ơn mọi người đã trả lời!

0

Có lẽ không được thông báo lỗi có ý nghĩa nhất, tôi đã nhìn thấy điều này xảy ra ở một số mã của bên thứ ba như dưới đây,

object obj = new object(); 
lock (obj) 
{ 
    //do something 

    Monitor.Exit(obj);//obj released 

}//exception happens here, when trying to release obj 
0

Tôi đã thấy điều này xảy ra khi bạn khóa mã sử dụng một màn hình, sau đó gọi một async mã và bạn nhận được điều này, khi sử dụng một khóa (đối tượng) bạn nhận được một lỗi trình biên dịch, tuy nhiên giữa monitor.enter (đối tượng) và Monitor.Exist (đối tượng) trình biên dịch không phàn nàn ... không may.

Các vấn đề liên quan