Способ сделать это с помощью CancellationToken и новой модели отмены.Новая модель отмены интегрирована в .NET Framework нескольких типов.Наиболее важными из них являются System.Threading.Tasks, System.Threading.Tasks.Task, System.Threading.Tasks.Task и System.Linq.ParallelEnumerable.
Вот пример вашей проблемы.Этот код всегда будет взаимоблокировать, потому что вызывающий код сначала берет блокировку, а затем заблокированная задача пытается получить такую же блокировку.
public void Example()
{
object sync = new Object();
lock (sync)
{
CancellationTokenSource canceller = new CancellationTokenSource();
ManualResetEvent started = new ManualResetEvent(false);
Task deadlocked = Task.Factory.StartNew(() =>
{
started.Set();
// EVIL CODE: This will ALWAYS deadlock
lock(sync) { };
},
canceller.Token);
// Make sure task has started.
started.WaitOne();
canceller.Cancel();
try
{
// Wait for task to cancel.
deadlocked.Wait();
}
catch (AggregateException ex)
{
// Ignore canceled exception. SIMPLIFIED!
if (!(ex.InnerException is TaskCanceledException))
throw;
}
}
}
Отмена задания в TPL является кооперативной.Другими словами, это всегда будет тупиком, потому что ничто не обрабатывает маркер отмены, установленный как отмененный, потому что поток задачи заблокирован.
Существует способ обойти это, но он все еще полагается на авторов ненадежного кода, чтобыправильная вещь:
public static void Example2()
{
Mutex sync = new Mutex(true);
CancellationTokenSource canceller = new CancellationTokenSource();
bool started = false;
Task deadlocked = Task.Factory.StartNew(() =>
{
started = true;
// EVIL CODE: This will ALWAYS deadlock
WaitHandle.WaitAny(new WaitHandle[] { canceller.Token.WaitHandle, sync });
},
canceller.Token);
// Make sure task has started.
while (!started) { }
canceller.Cancel();
try
{
// Wait for task to cancel.
deadlocked.Wait();
}
catch (AggregateException ex)
{
// Ignore canceled exception. SIMPLIFIED!
if (!(ex.InnerException is TaskCanceledException))
throw;
}
}
Указывает на заметку;Отмена является кооперативным.Вы можете использовать Token.WaitHandle, чтобы получить дескриптор и ждать его вместе с дескриптором (ами) других примитивов синхронизации.Mutex намного медленнее, чем Monitor (или блокировка).
Действительно, если вы не достаточно доверяете автору кода, чтобы он реализовал совместную отмену, тогда я бы поставил под сомнение целесообразность их запуска внутри вашего AppDomain.в той же теме.
Подробнее см .:
http://msdn.microsoft.com/en-us/library/dd997364.aspx
http://msdn.microsoft.com/en-us/library/dd537607.aspx
http://msdn.microsoft.com/en-us/library/ee191552.aspx