簡體   English   中英

c# - 等待2個線程中的1個完成

[英]c# - Waiting for 1 of 2 threads to be finished

我的代碼中有一個位置,我需要等待在傳感器上識別任一個手指,或者用戶按下一個鍵以中止此操作並返回主菜單。
我嘗試使用像Monitor和鎖定概念的條件變量,但當我嘗試提醒主線程時,沒有任何反應。

碼:

private static object _syncFinger = new object(); // used for syncing

private static bool AttemptIdentify()
{
    // waiting for either the user cancels or a finger is inserted
    lock (_syncFinger)
    {
        Thread tEscape = new Thread(new ThreadStart(HandleIdentifyEscape));
        Thread tIdentify = new Thread(new ThreadStart(HandleIdentify));
        tEscape.IsBackground = false;
        tIdentify.IsBackground = false;
        tEscape.Start();
        tIdentify.Start();
        Monitor.Wait(_syncFinger); // -> Wait part
    }

    // Checking the change in the locked object

    if (_syncFinger is FingerData) // checking for identity found
    {
        Console.WriteLine("Identity: {0}", ((FingerData)_syncFinger).Guid.ToString());
    }
    else if(!(_syncFinger is Char)) // char - pressed a key to return
    {
        return false; // returns with no error
    }

    return true;
}

private static void HandleIdentifyEscape()
{
    do
    {
        Console.Write("Enter 'c' to cancel: ");
    } while (Console.ReadKey().Key != ConsoleKey.C);
    _syncFinger = new Char();
    LockNotify((object)_syncFinger);
}

private static void HandleIdentify()
{
    WinBioIdentity temp = null;
    do
    {
        Console.WriteLine("Enter your finger.");
        try // trying to indentify
        {
            temp = Fingerprint.Identify(); // returns FingerData type
        }
        catch (Exception ex)
        {
            Console.WriteLine("ERROR: " + ex.Message);
        }
        // if couldn't identify, temp would stay null
        if(temp == null)
        {
            Console.Write("Invalid, ");
        }
    } while (temp == null);

    _syncFinger = temp;
    LockNotify(_syncFinger);
}

private static void LockNotify(object syncObject)
{
    lock(syncObject)
    {
        Monitor.Pulse(syncObject); 
    }
}

當我嘗試提醒主線程時,沒有任何反應。

那是因為主線程正在監視器上等待此處創建的對象:

private static object _syncFinger = new object(); // used for syncing

但是每個線程都會替換該對象值,然后向監視器發出對象的信號。 主線程不知道新對象,因此當然告知監視器該新對象對主線程沒有影響。

首先, readonly為了使用lock創建對象時,請將其readonly

private static readonly object _syncFinger = new object(); // used for syncing

它始終是正確的事情,這將阻止您在線程等待時更改被監視對象的錯誤。

接下來,創建一個單獨的字段來保存WinBioIdentity值,例如:

private static WinBioIdentity _syncIdentity;

並使用它將結果中繼回主線程:

private static bool AttemptIdentify()
{
    // waiting for either the user cancels or a finger is inserted
    lock (_syncFinger)
    {
        _syncIdentity = null;
        Thread tEscape = new Thread(new ThreadStart(HandleIdentifyEscape));
        Thread tIdentify = new Thread(new ThreadStart(HandleIdentify));
        tEscape.IsBackground = false;
        tIdentify.IsBackground = false;
        tEscape.Start();
        tIdentify.Start();
        Monitor.Wait(_syncFinger); // -> Wait part
    }

    // Checking the change in the locked object

    if (_syncIdentity != null) // checking for identity found
    {
        Console.WriteLine("Identity: {0}", ((FingerData)_syncIdentity).Guid.ToString());
        return true;
    }

    return false; // returns with no error
}

private static void HandleIdentifyEscape()
{
    do
    {
        Console.Write("Enter 'c' to cancel: ");
    } while (Console.ReadKey().Key != ConsoleKey.C);
    LockNotify((object)_syncFinger);
}

private static void HandleIdentify()
{
    WinBioIdentity temp = null;
    do
    {
        Console.WriteLine("Enter your finger.");
        try // trying to indentify
        {
            temp = Fingerprint.Identify(); // returns FingerData type
        }
        catch (Exception ex)
        {
            Console.WriteLine("ERROR: " + ex.Message);
        }
        // if couldn't identify, temp would stay null
        if(temp == null)
        {
            Console.Write("Invalid, ");
        }
    } while (temp == null);

    __syncIdentity = temp;
    LockNotify(_syncFinger);
}

所有這一切,你應該更喜歡使用現代的async / await習語:

private static bool AttemptIdentify()
{
    Task<WinBioIdentity> fingerTask = Task.Run(HandleIdentify);
    Task cancelTask = Task.Run(HandleIdentifyEscape);

    if (Task.WaitAny(fingerTask, cancelTask) == 0)
    {
        Console.WriteLine("Identity: {0}", fingerTask.Result.Guid);
        return true;
    }

    return false;
}

private static void HandleIdentifyEscape()
{
    do
    {
        Console.Write("Enter 'c' to cancel: ");
    } while (Console.ReadKey().Key != ConsoleKey.C);
}

private static WinBioIdentity HandleIdentify()
{
    WinBioIdentity temp = null;
    do
    {
        Console.WriteLine("Enter your finger.");
        try // trying to indentify
        {
            temp = Fingerprint.Identify(); // returns FingerData type
        }
        catch (Exception ex)
        {
            Console.WriteLine("ERROR: " + ex.Message);
        }
        // if couldn't identify, temp would stay null
        if(temp == null)
        {
            Console.Write("Invalid, ");
        }
    } while (temp == null);

    return temp;
}

以上是一個極少數的例子。 最好將AttemptIdentify()方法本身設置為async ,然后使用await Task.WhenAny()而不是Task.WaitAny() 包含一些機制來中斷任務也是更好的,即一旦完成任務,你應該想要打斷另一個,這樣就不會繼續嘗試它的工作。

但是這些問題並不是async / await版本所特有的,並且不需要解決以改進現有的代碼。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM