Это можно сделать с помощью 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 (или lock).
На самом деле, если вы недостаточно доверяете автору кода, чтобы они реализовали совместную отмену, я бы поставил под сомнение разумность их запуска внутри вашего 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
person
Ade Miller
schedule
06.05.2010