Adm*_*tor 3 c# multithreading mutex deadlock fingerprint
我的代码中有一个位置,我需要等待在传感器上识别任一个手指,或者用户按下一个键以中止此操作并返回主菜单.
我尝试使用条件变量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);
}
}
Run Code Online (Sandbox Code Playgroud)
当我尝试提醒主线程时,没有任何反应.
那是因为主线程正在监视器上等待此处创建的对象:
private static object _syncFinger = new object(); // used for syncing
Run Code Online (Sandbox Code Playgroud)
但是每个线程都会替换该对象值,然后向监视器发出新对象的信号.主线程不知道新对象,因此当然告知监视器该新对象对主线程没有影响.
首先,创建与使用的目的,对象的任何时间lock,使它readonly:
private static readonly object _syncFinger = new object(); // used for syncing
Run Code Online (Sandbox Code Playgroud)
它始终是正确的事情,这将阻止您在线程等待时更改被监视对象的错误.
接下来,创建一个单独的字段来保存WinBioIdentity值,例如:
private static WinBioIdentity _syncIdentity;
Run Code Online (Sandbox Code Playgroud)
并使用它将结果中继回主线程:
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);
}
Run Code Online (Sandbox Code Playgroud)
所有这一切,你应该更喜欢使用现代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;
}
Run Code Online (Sandbox Code Playgroud)
以上是一个极少数的例子.最好是制作AttemptIdentify()方法async本身,然后使用await Task.WhenAny()而不是Task.WaitAny().包含一些机制来中断任务也是更好的,即一旦完成任务,你应该想要打断另一个,这样就不会继续尝试它的工作.
但是这些问题并不是async/ awaitversion 独有的,并且不需要解决来改进你现在拥有的代码.