Thread.Abort и альтернативы - PullRequest
4 голосов
/ 30 июля 2010

Это больше из личного любопытства / интереса, чем из-за конкретной проблемы, которую я пытаюсь решить.

Предположим, у вас есть программа, которая выполняет некоторую операцию над предоставленной пользователем информацией (например, строкой поиска), которая изменяется по мере того, как пользователь ее вводит. Предположим, что вы хотите показать пользователю наиболее релевантную информацию для того, что он набрал в любой момент времени.

Если бы потоки действительно прерывались, мы могли бы просто запустить один поток на основе последней измененной строки поиска и отменить все предыдущие потоки, которые были в процессе.

Теперь общепринятая лучшая практика для этого сегодня заключается в использовании таймера задержки в качестве пользовательских типов, который ждет от 0,5 до 1 секунды, прежде чем начать операцию. Я надеюсь, что достаточно очевидно, что это не идеальное решение, теоретически (любой вид искусственной задержки создает искусственное узкое место, которое никогда не может быть преодолено, даже если оно составляет всего 0,5 секунды).

Кроме того, в соответствии с лучшими на сегодня практиками утверждается, что любые последующие операции должны ожидать завершения предыдущей операции перед выполнением. В мире, где мы не можем прервать операции, это имеет смысл, но, теоретически, это далеко от идеала. Представьте, что пользователь вводит один символ и делает паузу достаточно долго, чтобы начать операцию. Предположим, что для выполнения этой операции требуется 10 секунд. Теперь пользователь вынужден ждать недопустимое количество времени, прежде чем результаты его запроса станут видны.

(Необязательный) обходной путь - это одновременное выполнение нескольких операций, предполагая, что это безопасно, но это все равно приведет к значительному снижению производительности.

Так что мне просто интересно узнать мнение людей по этому поводу, по крайней мере, специфическое для .NET, и есть ли какие-либо новые разработки в этой области с тех пор, как я в последний раз исследовал это, о которых мне следует знать (возможно, параллельные библиотеки?) Мне также было бы любопытно узнать, могут ли какие-либо другие языки / фреймворки справляться с таким детальным управлением операциями лучше, чем .NET.

Приветствие.

Ответы [ 4 ]

4 голосов
/ 30 июля 2010

Вы должны взглянуть на параллельную библиотеку задач в .NET 4 и новую модель отмены . Задачи могут выполняться одновременно, и их можно безопасно отменить. Похоже, хорошо подходит для того, что вам нужно.

3 голосов
/ 30 июля 2010

С улучшениями .Net 4 для Параллельное программирование , например, TPL и , как упомянуто Марком Байерсом.«Никогда не прерывайте поток! Если вы точно не знаете, что делаете ... Даже не надо».К счастью, правило было смягчено.

Еще один инструмент, который допускает более «композиционный» подход, - это Reactive Extensions for .NET (Rx) .С домашней страницы проекта ..

Rx - это библиотека для составления асинхронных программ и программ, основанных на событиях, с использованием наблюдаемых коллекций.

Недавно они выпустили практическую лабораторию.....

Лечение асинхронного блюза с помощью Reactive Extensions для .NET

..., который включает решение, которое при странном совпадении времени обращаетсяваш пример ... "Предположим, что вы хотите показать пользователю наиболее релевантную информацию о том, что он набрал в любой момент времени." ... и необходимость отменить / отменить / остановить задачу в процессе.

Вкратце из лаборатории ...

В нашем текущем примере мы создаем простое приложение для подсказки словаря.После того, как пользователь введет условие поиска, приложение отключит вызов веб-службы, чтобы получить предложения слов обратно.Поскольку мы не хотим блокировать пользовательский интерфейс, мы также хотим поддерживать асинхронную связь со службой словаря.

Следуя прекрасному пошаговому решению, получим ...

var txt = new TextBox(); 
var lst = new ListBox { Top = txt.Height + 10 }; 
var frm = new Form { 
    Controls = { txt, lst } 
}; 

// Turn the user input into a tamed sequence of strings. 
var textChanged = from evt in Observable
                  .FromEvent<EventArgs>(txt, "TextChanged") 
                  select ((TextBox)evt.Sender).Text; 

var input = textChanged 
            .Throttle(TimeSpan.FromSeconds(1)) 
            .DistinctUntilChanged(); 

// Bridge with the web service's MatchInDict method. 
var svc = new DictServiceSoapClient("DictServiceSoap"); 
var matchInDict = Observable
                  .FromAsyncPattern<string, string, string, DictionaryWord[]> 
                  (svc.BeginMatchInDict, svc.EndMatchInDict); 

Func<string, IObservable<DictionaryWord[]>> matchInWordNetByPrefix = 
    term => matchInDict("wn", term, "prefix"); 

// The grand composition connecting the user input with the web service. 
var res = from term in input 
          from word in matchInWordNetByPrefix(term).TakeUntil(input) 
          select word; 

// Synchronize with the UI thread and populate the ListBox or signal an error. 
using (res.ObserveOn(lst).Subscribe( 
    words => { 
        lst.Items.Clear(); 
        lst.Items.AddRange((from word in words select word.Word).ToArray()); 
    }, 
    ex => { 
        MessageBox.Show("An error occurred: " + ex.Message, frm.Text, 
                        MessageBoxButtons.OK, MessageBoxIcon.Error); 
    })) 
{ 
    Application.Run(frm); 
} // Proper disposal happens upon exiting the application. 

Наслаждайтесь.

2 голосов
/ 30 июля 2010

По сути, чистый способ прервать задачу - это красиво попросить ее, а затем позволить ей изящно закрыться.Это может быть с флагом, токеном отмены (например, Parallel Extensions) или чем-то подобным.

Это работает только тогда, когда вы контролируете задачу - или когда она уже запрограммирована для этого - но этополезен в большом количестве ситуаций.

Я не согласен с «дождаться завершения предыдущей операции перед выполнением следующей» в целом.Конечно, это зависит от операции - но если вы можете запустить две операции параллельно, можете помешать завершению первой из них запутаться, и не против того, чтобы первая продолжала работать доон замечает флаг «остановите, пожалуйста», который вы только что установили в соответствии с первым абзацем этого ответа, это нормально.Хотя это сильно зависит от ситуации.

1 голос
/ 30 июля 2010

То, что вы не должны злоупотреблять Thread.Abort(), не означает, что вы не можете отменить операцию в фоновом потоке.

// set to true to cancel
volatile bool cancel;

// background thread function
void foo()
{
    bool done = false;

    while (!done && !cancel)
    {
        ...
    }
}

Хитрость заключается в том, чтобы позволить фоновому потоку завершить работу чисто , не неожиданно.

...