消息及異步處理

    程序中在運行時,多線程往往會帶來許多意想不到的情況,比如下面一個關於消息收發的例子可以說明。
    代碼段一:
    msg.Return = this.SendSysMsg(new CMsg(MTAppPatientMgrPlugIn.QueryPatients, strSqlWhere, strSqlOrder));
    if(null == msg.Return)
    {
       return;
    }
    this.m_hsOperHandle.Add(msg.Return, msg.Return);
    這段代碼的功能就是發送一個查詢病人的消息,並接受一個返回值(操作句柄)加入到哈希表中。

    監聽此消息的服務程序在執行完查詢操作後會發回一個反饋消息,下面的代碼就是返回消息處理:
    代碼段二:
    if(this.m_hsOperHandle.Contains(msg.LParam))
    {
        this.PostSysMsg(new CMsg(MTFetusWardPlugIn.QueryPregnantWomansResult_InPlugIn, msg));
        this.m_hsOperHandle.Remove(msg.LParam);
    }
    此段代碼的功能就是判斷哈希表中是否有發送消息所返回的句柄對象,如果有就說明此反饋消息是當前類中所發消息的反饋。此時就作消息的處理,處理完後移除該句柄對象。
    這兩段代碼看起來沒有什麼不對,發送消息後接受消息反饋處理,並以唯一的句柄對象來區別消息對。但運行起來後結果並不是這樣,單步時發現消息已正常發出去了,反饋消息也接收了,但在消息接收處理即代碼段二中,在哈希表中並沒有找到該消息對的句柄對象。

    爲什麼消息正常發出了句柄卻沒有加到集合(哈希表)中呢,不可能啊,this.m_hsOperHandle.Add(msg.Return, msg.Return);這一步也有執行過。那唯一的可能就是在接受反饋消息並判斷集合中是否有句柄的處理,是在往集合中添加句柄處理之前執行的。那這樣就可能了,因爲本次操作不止一個線程在處理,問題肯定是出在多線程處理上。
    接下來修改程序,將這兩段代碼的操作資源鎖定同步起來,運行起來時果然解決此問題了,改動如下。
    代碼段一:
    lock(m_hsOperHandle.SyncRoot)
    {
        msg.Return = this.SendSysMsg(new CMsg(MTAppPatientMgrPlugIn.QueryPatients, strSqlWhere, strSqlOrder));
        if(null == msg.Return)
        {
            return;
       }
       this.m_hsOperHandle.Add(msg.Return, msg.Return);
    }

    將發送消息返回句柄對象,和判斷是否爲null並加入集合這幾步操作鎖定。
    代碼段二:
    lock(m_hsOperHandle.SyncRoot)
    {
        if(this.m_hsOperHandle.Contains(msg.LParam))
        {
            this.PostSysMsg(new CMsg(MTFetusWardPlugIn.QueryPregnantWomansResult_InPlugIn, msg));
            this.m_hsOperHandle.Remove(msg.LParam);
        }
    }
    同理,以鎖定要訪問資源m_hsOperHandle的同步對象,來實現發送消息與接受消息同步。

    發生此問題的根本原因就是,在線程1發送消息並返回操作句柄對象後,並不能保證就馬上將句柄加入集合,因爲此時另一個線程2在接受到此消息後也在進行處理,這時可能這個線程先處理完,併發送反饋消息。此時線程1接受到此反饋消息並開始處理,但線程1中發送消息所返回的句柄還沒有加入到集合中,所以反饋消息處理時集合中找不到該句柄,而最終導致操作異常。

    代碼lock(m_hsOperHandle.SyncRoot)保證代碼段一的原子性,即保證此塊的代碼能全部操作完。即使還沒有操作完時,代碼段二的代碼被調用,但這兩處的操作已鎖定相同的資源m_hsOperHandle,在代碼段一還沒有處理完,即還沒有解鎖前,調用代碼段二的線程就會被掛起,至到代碼段一處理完並解鎖後纔會執行,那麼此時集合中鐵定有該消息的句柄對象了。所以此鎖定資源的方式,保證了程序執行的正常順利。 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章