ядро dotnet эквивалентно Thread.Abort - PullRequest
0 голосов
/ 25 ноября 2018

Фон

У меня есть Service абстракция.Каждый сервис имеет свой собственный WorkItem.WorkItem может начать с некоторых данных.Услуга ограничивает время исполнения WorkItem.Допустим, один рабочий элемент может занять до 60 секунд.После этого Service должен убить его.

Этот код перенесен из Standard .NET Framework , я создал объект Thread, который запускает метод Start(model).Тогда код был что-то вроде:

Thread t = new Thread(workItem.Start, model);
t.start();
if (!t.Join(TimeSpan.FromSeconds(60)))
    t.Abort();

Thread.Abort выдавал исключение для запущенного потока, что приводило к немедленной остановке.

Теперь, Я переместил код в ядро ​​dotnet - как вы, возможно, знаете, когда вы звоните Thread.Abort(), вы получаете следующее сообщение:

System.PlatformNotSupportedException: Thread abort is not supported on this platform.
   at System.Threading.Thread.Abort()
   at ...

Цель

Я хочу ограничить время выполнения WorkItem определенным количеством времени.Обратите внимание, что это ограничение должно работать и в том случае, если вы запускаете строку кода следующим образом:

Thread.sleep(61000); // 61 seconds. should be stop after 60 seconds.

Progress

В мире ядра dotnet кажется, что оно идет к решению, связанному с Task.Итак, я подумал использовать CancellationToken.Но кажется невозможным наблюдать за событием "Cacneled" и немедленно остановиться.Примеры, которые я видел, используют циклы while (!canceled), которые не могут остановить длинные операции (например, Thread.Sleep(1000000).

Вопрос

Как это сделать правильно?

Обновление

Я написал этот пример кода:

public static bool ExecuteWithTimeLimit(TimeSpan timeSpan, Action codeBlock)
{
    try
    {
        Task task = Task.Factory.StartNew(() => codeBlock());
        if (!task.Wait(timeSpan))
        {
            // ABORT HERE!
            Console.WriteLine("Time exceeded. Aborted!");
        }
        return task.IsCompleted;
    }
    catch (AggregateException ae)
    {
        throw ae.InnerExceptions[0];
    }
}

И этот Main файл:

public static void Main(string[] args)
{
    bool Completed = ExecuteWithTimeLimit(TimeSpan.FromMilliseconds(2000), () =>
    {
        Console.WriteLine("start");
        Thread.Sleep(3000);
        Console.WriteLine("end");
    });

    Console.WriteLine($"Completed={Completed}");
    Console.ReadLine();
}

Ожидается: «конец» не будет выводиться на экран. Фактический: «end "напечатано. Есть ли альтернатива, которая может убить Task?

1 Ответ

0 голосов
/ 25 ноября 2018

Не прерывая, единственное решение - достаточно часто опрашивать запрос на отмену, чтобы после всего упомянутого вами решения while (!canceled).

Примеры, которые я видел, используют циклы while (!canceled), которые не могут остановитьсядлинные операции (например, Thread.Sleep(1000000).

Это только частично верно. Например, это может быть переписано так, чтобы быть отзывчивым:

 var timeout = TimeSpan.FromSeconds(60);
 var stopwatch = new Stopwatch();
 stopwatch.Start();

 while (!cancelToken.IsCancellationRequested
  && stopwatch.ElapsedMilliseconds < timeout)
{
    Thread.Sleep(10);
}

Конечно,не каждая задача может быть легко переписана для опроса отмены следующим образом. Если вы находитесь в глубокой цепочке вызовов, может быть затруднительно проверить отмену на каждом уровне. По этой причине вы также можете использовать метод CancellationToken.ThrowIfCancellationRequested,который выдаст OperationCanceledException, если был запрос на отмену. Обычно я склонен не генерировать исключение только для себя и использовать его для потока управления, но отмена - одна из областей, где это может быть оправдано.

Это решение, конечно, имеет некоторые ограничения по сравнению с Abort:

  • Вы не сможете отменить сторонние процедуры, которые не поддерживают отменуи вы не можете реорганизовать их
  • OperationCanceledException можно легко проглотить, тогда как ThreadAbortException всегда повторно вызывался в конце блоков catch, поэтому библиотека 3-й части могла быть с большой вероятностью прерванадаже если он содержит общие блоки улова.

Обновление:

Если вы достаточно уверены или отчаялись, вы можете использовать ThreadEx.Abortметод, который вызывает Thread.AbortInternal путем отражения.Хотя это не гарантировано, это будет долгоживущее решение в .NET Core.

Хотя я не совсем согласен с тем, чтобы сделать Thread.Abort устаревшим, так как это был хороший инструмент последнего шанса для закрытия подпрограмм.на что вы не оказали влияния иначе, я также на стороне, аборт нужно избегать любой ценой, так как он может иметь неприятных побочных эффектов .Если вы являетесь автором всей кодовой базы, этого всегда можно избежать.

Обновление 2:

Похоже, с тех пор AbortInternal было удалено.По крайней мере текущий источник .NET Core не содержит такого метода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...