【问题标题】:c# - Waiting for 1 of 2 threads to be finishedc# - 等待 2 个线程中的 1 个完成
【发布时间】:2018-07-10 22:09:55
【问题描述】:

我的代码中有一个位置,我需要等待在传感器上识别出一个手指,或者用户按下一个键以中止此操作并返回主菜单。
我尝试使用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); 
    }
}

【问题讨论】:

  • 你应该使用Task而不是线程;然后只需使用Task.WhenAny()
  • Fingerprint.Identify() 阻塞了吗?

标签: c# multithreading mutex deadlock fingerprint


【解决方案1】:

当我尝试提醒主线程时,什么也没有发生。

那是因为主线程正在监视器上等待这里创建的对象:

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

但是您的每个线程都会替换该对象值,然后向监视器发出 new 对象的信号。主线程不知道新对象,因此当然向监视器发出该新对象的信号不会对主线程产生影响。

首先,任何时候你为了使用lock而创建一个对象,把它设为readonly

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

这样做总是正确的,这将防止您在线程等待时更改受监视对象的错误。

接下来,创建一个单独的字段来保存 WinBioIdentity 值,例如:

private static WinBioIdentity _syncIdentity;

并使用 that 将结果传递回主线程:

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版本独有的,不需要解决来改进你现在的代码。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-07-18
    • 2010-11-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多