代码之家  ›  专栏  ›  技术社区  ›  Administrator

c#-等待完成2个线程中的1个

  •  3
  • Administrator  · 技术社区  · 6 年前

    我的代码中有一个位置,我需要等待传感器上的任一手指被识别,或者用户按键中止此操作并返回主菜单。
    我试着用条件变量之类的东西 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); 
        }
    }
    
    1 回复  |  直到 6 年前
        1
  •  5
  •   Peter Duniho    6 年前

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

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

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

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

    首先,无论何时创建对象以用于 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() 方法 异步 本身,然后使用 await Task.WhenAny() 而不是 Task.WaitAny() . 最好包括一些中断任务的机制,也就是说,一旦一个任务完成,你应该想中断另一个任务,这样它就不会到处乱跑,继续尝试它的工作。

    但这些问题并不是 异步 / 等候 版本,无需解决即可改进现有代码。