前天寫了一篇 非同步方法之同步化及逾時機制,與chicken大人討論程式碼中Polling的部分(指每隔一段時間檢查一次結果的輪詢寫法),固然邏輯簡單,但效率與精確度不優,但若被呼叫的元件本身不提供"完成後主動通知"的機制,Polling雖然討厭,卻也無法避免!

若被呼叫端在完成工作時,可透過事件回呼方式通知呼叫方,則整個局勢就改觀了! 於是索性也提筆敲鍵練習一番,補充"完成後會回Call"情境的做法。

如下例,我們在AsyncJobClass完成工作後,新增主動回Call AfterFinished事件通知呼叫方的功能(橘底部分),如此便可引用AutoResetEvent來做同步。做法是先宣告一個AutoResetEvent,並在AfterFinished事件中呼叫AutoResetEvent.Set()。則DoAsyncJob()後,我們便可使用AutoResetEvent.WaitOne(TimeoutMilliSeconds)等待工作完成觸發AutoResetEvent.Set()並指定逾時上限,整個邏輯頓時簡化許多。

程式碼如下,恭請參考指教。

using System;
using System.Threading;
 
class Program
{
    static AutoResetEvent sync = new AutoResetEvent(false);
    static void Main(string[] args)
    {
        AsyncJobClass.Ready = false;
        //set AutoResetEvent after finished
        AsyncJobClass.AfterFinished = () =>
        {
            sync.Set();
        };
 
        try
        {
            AsyncJobClass.Ready = false;
            AsyncJobClass.DoAsyncJob(false);
            //Test 2, Timeout = 3s
            sync.WaitOne(3000);
            if (!AsyncJobClass.Ready)
                throw new ApplicationException("Timeout!");
        }
        catch (Exception e)
        {
            Console.WriteLine("Error=" + e.Message);
            Console.WriteLine("Test 2, longer than timeout, OK!");
        }
        Console.Read();
    }
}
 
 
class AsyncJobClass
{
    public static bool Ready = false;
    //Simulating async work
    //delay 5 sec, than set Ready = true, or throw an exception in purpose
    public static void DoAsyncJob(bool raiseException)
    {
        ThreadPool.QueueUserWorkItem((o) =>
        {
            if (raiseException)
                OnError(new ApplicationException("Exception in Purpose!"));
            else
            {
                Thread.Sleep(5000);
                Ready = true;
                AfterFinished();
            }
        });
    }
    //The event triggered when exception raised
    public static Action<Exception> OnError;
    //The event triggered after finished
    public static Action AfterFinished;
}

【補充參考】


Comments

# by laneser

事實上我都這麼寫的: static void Main(string[] args) { Thread t = new Thread(() => { Thread.Sleep(5000); Console.WriteLine("done!"); }); t.Start(); bool IsDone = t.Join(3000); Console.WriteLine("test is longer than timeout"); Console.Read(); }

Post a comment