如何在c#任务并行库中终止特定线程

本文关键字:终止 线程 并行 任务 | 更新日期: 2023-09-27 18:09:52

我有一项棘手的任务,我一直试图安静地完成一段时间,但直到现在我还想不出任何办法来完成它。无论如何,这是一个场景…

我有一个包含列表视图和按钮的winform应用程序。listview包含1列,其中包含我需要稍后传递给我的函数的数据。列包含50行,其中包含一个链接列表。

现在我有这个函数,我用它来获取和抓取这些链接的内容(5个链接一次)与并行多线程模式使用(任务并行库):

//List<int> currentWorkingItem //contains the indices of the items in listview
//List<string> URLsList //contains the URLs of the items in listview
Parallel.ForEach(URLsList, new ParallelOptions() { MaxDegreeOfParallelism = 5 }, (url, i, j) =>
{
    //show to user this link is currently being downloaded by highlighting the item to green...
    this.BeginInvoke((Action)(delegate()
    {
        //current working item 
        mylistview.Items[currentWorkingItem[(int)j]].BackColor = green;
    }));
    //here I download the contents of every link in the list...
    string HtmlResponse = GetPageResponse(url);
    //do further processing....
});

现在上面的代码工作得很好…但有时我希望用户中止当前正在运行的某些线程,并继续使用列表中的其他线程…这是可以实现的吗?如果是的话,请帮助我。我非常感谢任何解决方案或建议。

如何在c#任务并行库中终止特定线程

尝试使用带有取消令牌的任务库。我觉得这样做更优雅更安全。下面是一个很好的例子:

using System;
using System.Threading.Tasks;
using System.Threading;
namespace CancelTask
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Press 1 to cancel task");
            var cTokenSource = new CancellationTokenSource();
            // Create a cancellation token from CancellationTokenSource
            var cToken = cTokenSource.Token; 
            // Create a task and pass the cancellation token
            var t1 = Task<int>.Factory.StartNew(() 
                => GenerateNumbers(cToken), cToken);
            // to register a delegate for a callback when a 
            // cancellation request is made
            cToken.Register(() => cancelNotification());
            // If user presses 1, request cancellation.
            if (Console.ReadKey().KeyChar == '1')
            {
                // cancelling task
                cTokenSource.Cancel();
            }
            Console.ReadLine();
        }
        static int GenerateNumbers(CancellationToken ct)
        {
            int i;
            for (i = 0; i < 10; i++)
            {
                Console.WriteLine("Method1 - Number: {0}", i);
                Thread.Sleep(1000);
                // poll the IsCancellationRequested property
                // to check if cancellation was requested
                if (ct.IsCancellationRequested)
                {
                    break;
                }
            }
            return i;
        }
        // Notify when task is cancelled
        static void cancelNotification()
        {
            Console.WriteLine("Cancellation request made!!");
        }
    }
}

原文可在此找到:http://www.dotnetcurry.com/ShowArticle.aspx?ID=493

好了,经过一番努力,我终于找到了一个有效而简单的解决方案。

它只需要一个哈希表,其中包含列表视图中所选项目的索引和一个简单的bool值。索引是键,bool (true, false)是值。bool值就像一个(on/off)开关,表示当前循环是否中止。因此,为了中止特定的线程简单,我需要将我的listview中选定项目的键(索引)传递给foreach循环,并检查bool开关是否打开或关闭,这基本上就是它…

所以我的最终代码是这样的:
//I declared the hashtable outside the function so I can manage it from different source.
    private Hashtable abortingItem;

现在,当我点击抓取按钮,它应该填充哈希表与选定的索引…

abortingItem = new Hashtable();
for (int i = 0; i < myURLslist.SelectedItems.Count(); i++)
{
    //false means don't abort this.. let it run
    abortingItem.Add(myURLslist.SelectedItems[i].index, false);
}
//here should be the code of my thread to run the process of grabbing the URLs (the foreach loop)
//..........................

现在,如果我需要中止特定的项目,我所需要做的就是在列表视图中选择项目,然后点击中止按钮

private void abort_Click(object sender, EventArgs e)
{
    if (abortingItem != null)
    {
        for (int u = 0; u < myURLslist.SelectedIndices.Count; u++)
        {
            //true means abort this item
            abortingItem[myURLslist.SelectedIndices[u]] = true;
        }
    }
}

在我的foreach循环中,我所需要的只是一个简单的if else语句来检查bool是打开还是关闭:

//List<int> currentWorkingItem //contains the indices of the items in listview
//List<string> URLsList //contains the URLs of the items in listview
Parallel.ForEach(URLsList, new ParallelOptions() { MaxDegreeOfParallelism = 5 }, (url, i, j) =>
{
//aborting
if (!(bool)abortingItem[currentWorkingItem[(int)j]])
{
    //show to user this link is currently being downloaded by highlighting the item to green...
    this.BeginInvoke((Action)(delegate()
    {
        //current working item 
        mylistview.Items[currentWorkingItem[(int)j]].BackColor = green;
    }));
    //here I download the contents of every link in the list...
    string HtmlResponse = GetPageResponse(url);
    //do further processing....
}
else
{
  //aborted
}
});