Thread.Abort和替代方案

4

这更多是出于个人好奇/兴趣,而不是我要解决的具体问题。

假设您有一个程序,它正在对用户提供的信息(例如搜索字符串)执行某些操作,并且随着用户输入而发生变化。假设您想在任何给定时间向用户显示与他们键入的内容最相关的信息。

如果线程确实可以中止,我们可以仅运行一个基于最后更改的搜索字符串的线程,并取消任何先前正在进行的线程。

现在,今天通常接受的最佳实践是使用延迟计时器作为用户键入的操作之前等待0.5到1秒钟的时间。从理论上讲,我希望这足够明显了(任何形式的人工延迟都会施加人工瓶颈,即使只有0.5秒,也永远无法克服)。

此外,今天的最佳实践还指出,任何后续操作都应在执行之前等待前一个操作完成。在我们无法中止操作的情况下,这是有道理的,但从理论上讲,这远非理想。想象一下,用户键入一个字符并暂停足以开始操作。假设此操作需要10秒钟才能执行。现在,用户必须等待不可接受的时间,才能看到他/她的查询结果。

这种情况下(并不理想),解决方法是同时执行多个操作,假设可以安全地这样做,但这仍然会导致性能显著降低。

因此,我只是想知道人们对此的看法,至少是针对.NET,并且是否有任何新的发展在这个领域,自从我上次研究它以来,我应该知道(例如并行库)?我也很想知道是否有任何其他语言/框架可以比.NET更好地处理这种精细的操作控制。

干杯。

4个回答

4
你应该看一下.NET 4中的任务并行库和新的取消模型。任务可以并发运行,而且安全地取消它们。看起来很适合你的需求。

3

随着 .Net 4 的增强功能,如 Parallel Programming 中的 TPLcancellation,正如 Mark Byers 所提到的。"除非你确切知道自己在做什么,否则永远不要中止线程!即使你知道也不要这样做。" 这个规则已经被放宽,这是一个好消息。

另一个允许更加 "组合式" 方法的工具是 Reactive Extensions for .NET (Rx)。从项目主页可以看到...

Rx 是一个使用可观察集合来组合异步和基于事件的程序的库。

他们最近发布了一个实践操作室....

使用Reactive Extensions解决.NET异步问题

...其中包括一个解决方案,巧合的是,它解决了你的例子... "假设你想为用户显示他们在任何给定时间键入的最相关信息。"..和需要取消/中止/停止正在进行的任务。

简要介绍实验室...

在我们的样例中,我们正在构建一个简单的字典建议应用程序。当用户输入搜索词时,应用程序将发出调用以从Web服务获取单词建议。由于我们不希望阻塞UI,因此我们还希望与字典服务的通信也是异步的。

按照优秀的逐步说明,得到的解决方案是...

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
基本上,优雅地中止任务的方法是要求它停止,并让它自行优雅地关闭。这可以通过标志、取消令牌(例如并行扩展)或类似方式完成。
这仅适用于您控制任务或已经编程执行此操作的情况,但在许多情况下非常有用。
一般来说,我不赞成“在执行下一个操作之前等待上一个操作完成”的做法。当然,这取决于操作 - 但如果您可以并行运行两个操作,可以防止第一个操作的完成搞乱事情,并且不介意第一个操作继续运行直到它注意到您刚刚设置的“停止请”标志,那就没问题了。不过这很大程度上取决于具体情况。

1

仅仅因为你不应该滥用 Thread.Abort() 并不意味着你不能在后台线程中取消一个操作。

// set to true to cancel
volatile bool cancel;

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

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

关键是让你的后台线程“干净”退出,而不是意外退出。


尽管如此,它让我想起了合作式多任务处理的日子。还记得抢占式多任务处理出现时有多么令人兴奋吗?我猜想完全安全地杀死任务的唯一方法就是分离子进程,但这对我的开销来说太大了。 - devios1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接