如何使用C#中止任务并行库中的特定线程

时间:2011-09-25 17:16:55

标签: c# multithreading task-parallel-library abort

我有一个棘手的任务,我一直试图安静,但到现在为止,我想不出任何让它起作用的东西。无论如何这是场景...

我有一个winform应用程序包含listview和一个按钮。 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....
});

现在上面的代码工作得很好......但有时我希望用户中止当前正在运行的某个线程并继续列表中的其余线程......这是可以实现的吗?如果是这样,请帮助我..我真的很感激任何解决方案或建议..

2 个答案:

答案 0 :(得分:2)

尝试使用带取消令牌的任务库。我发现做你的事情更优雅,更安全。这是一个很好的例子:

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

答案 1 :(得分:-1)

在努力解决这个问题后,我终于找到了一个有效且简单的解决方案..

它只需要一个哈希表,其中包含listview中所选项目的标记和一个简单的bool值。索引是键,bool(true,false)是值。 bool值就像一个(开/关)开关指示当前循环是否中止..所以为了中止特定的线程简单,我需要将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
}
});

那就是它......