实现线程队列/等待,怎么样?

时间:2010-11-12 14:21:17

标签: c# multithreading

我有一个计时器每隔15分钟调用一个函数,这个函数计算我的DGV中的行数并为每行(另一个函数)启动一个线程,所述线程解析一个可能需要1的网页第二到第10秒完成。

虽然它确实可以正常工作,因为它有1-6行,但是会导致请求超时。

我希望它等待新创建的线程完成处理,然后再回到循环中创建另一个线程而不锁定主UI

                for (int x = 0; x <= dataGridFollow.Rows.Count - 1; x++)
                {
                    string getID = dataGridFollow.Rows[x].Cells["ID"].Value.ToString();
                    int ID = int.Parse(getID);
                    Thread t = new Thread(new ParameterizedThreadStart(UpdateLo));
                    t.Start(ID);
                    // <- Wait for thread to finish here before getting back in the for loop
                }

我在过去24小时内搜索了很多内容,阅读了很多关于这个特定问题及其实现的内容(Thread.Join,ThreadPools,Queuing,甚至是SmartThreadPool)。

我可能已经在某处阅读了正确的答案,但是我对C#的使用感到不安,那些是那些线程工具

感谢您的时间

6 个答案:

答案 0 :(得分:0)

直接调用方法或执行while循环(使用sleep调用)来检查线程的状态。

还有异步事件,但会调用另一种方法,并且您希望从同一点继续。

答案 1 :(得分:0)

我不知道为什么请求会超时。这听起来像是一个不同的问题。但是,我可以就您当前的方法提出一些建议。

  • 避免在具有非确定性边界的循环中创建线程。创建线程有很多开销。如果事先不知道操作次数,请改用ThreadPoolTask Parallel Library
  • 您不会通过使用Thread.Join阻止UI线程来获得所需的行为。导致UI无法响应,它将有效地序列化操作并取消您希望通过线程获得的任何优势。

如果您真的想限制并发操作的数量,那么更好的解决方案是创建一个单独的专用线程来启动操作。该线程将无限期地围绕循环旋转,等待项目出现在队列中,当它们这样做时,它将使它们出列并使用该信息异步启动操作(再次使用ThreadPool或TPL)。出队线程可以包含用于限制并发操作数量的逻辑。搜索有关生产者 - 消费者模式的信息,以便更好地了解如何实现这一点。

有一点学习曲线,但谁说线程很容易?

答案 2 :(得分:0)

考虑使用异步CTP。这是微软最近发布的下载异步模式。它应该极大地简化异步编程。链接是http://msdn.microsoft.com/en-us/vstudio/async.aspx。 (首先阅读白皮书)

您的代码如下所示。 (我还没有验证我的语法,抱歉)。

private async Task DoTheWork()
{
    for(int x = 0; x <= dataGridFollow.Rows.Count - 1; x++) 
    { 
        string getID = dataGridFollow.Rows[x].Cells["ID"].Value.ToString(); 
        int ID = int.Parse(getID); 
        task t = new Task(new Action<object>(UpdateLo), ID); 
        t.Start();
        await t;
    }
} 

此方法返回一个可以定期检查完成的任务。这遵循“火与忘记”的模式,这意味着你只需要调用它,大概是你完全不关心它(只要它在15分钟之前完成)。

修改
我更正了上面的语法,您需要更改UpdateLo以获取对象而不是Int。

答案 3 :(得分:0)

为了避免UI冻结,框架明确地为这些目的提供了一个类:看看 BackgroundWorker 类(在单独的线程上执行操作),这里有一些信息:{{3} } http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx

Btw看起来我是否理解你不想并行化任何操作,所以只需等待解析页面的方法完成。基本上,对于网格的每个(foreach look)行,您将获得id并调用方法。如果你想并行,只需重复使用相同的foreach循环并添加make it Parallel

http://msdn.microsoft.com/en-us/magazine/cc300429.aspx

答案 4 :(得分:0)

如果我理解正确的话,你现在正在做的是在UI线程中循环一个ID列表,启动一个新线程来处理每个线程。你看到的阻塞问题很可能就是它需要太多的资源才能创建独特的线程。所以,个人(不知道更多)会像这样重新设计这个过程:

//Somewhere in the UI Thread
Thread worker = new Thread(new ParameterizedThreadStart(UpdateLoWorker));
worker.Start(dataGridFollow.Rows);

//worker thread
private void UpdateLoWorker(DataRowCollection rows)
{
   foreach(DataRow r in rows){
      string getID = r.Cells["ID"].Value.ToString();
      int ID = int.Parse(getID);
      UpdateLo(ID);
   }
}

这里你有一个非阻塞工作者,它顺序处理每个ID。

答案 5 :(得分:0)

你想要的是引导一些做任务的工人。

当一个人完成后,你可以开始新的一个。

我确信有更好的方法可以使用线程池或其他什么......但我很无聊所以我想出了这个。

using System;
using System.Collections.Generic;
using System.Linq;
using System.ComponentModel;
using System.Threading;

namespace WorkerTest
{
    class Program
    {
        static void Main(string[] args)
        {
            WorkerGroup workerGroup = new WorkerGroup();

            Console.WriteLine("Starting...");

            for (int i = 0; i < 100; i++)
            {
                var work = new Action(() => 
                { 
                    Thread.Sleep(1000); //somework
                });

                workerGroup.AddWork(work);
            }

            while (workerGroup.WorkCount > 0)
            {
                Console.WriteLine(workerGroup.WorkCount);
                Thread.Sleep(1000);
            }

            Console.WriteLine("Fin");

            Console.ReadLine();
        }
    }


    public class WorkerGroup
    {
        private List<Worker> workers;

        private Queue<Action> workToDo;

        private object Lock = new object();

        public int WorkCount { get { return workToDo.Count; } }

        public WorkerGroup()
        {
            workers = new List<Worker>();
            workers.Add(new Worker());
            workers.Add(new Worker());

            foreach (var w in workers)
            {
                w.WorkCompleted += (OnWorkCompleted);
            }

            workToDo = new Queue<Action>();
        }

        private void OnWorkCompleted(object sender, EventArgs e)
        {
            FindWork();
        }

        public void AddWork(Action work)
        {
            workToDo.Enqueue(work);
            FindWork();
        }

        private void FindWork()
        {
            lock (Lock)
            {
                if (workToDo.Count > 0)
                {
                    var availableWorker = workers.FirstOrDefault(x => !x.IsBusy);
                    if (availableWorker != null)
                    {
                        var work = workToDo.Dequeue();
                        availableWorker.StartWork(work);
                    }
                }
            }
        }
    }

    public class Worker
    {
        private BackgroundWorker worker;

        private Action work;

        public bool IsBusy { get { return worker.IsBusy; } }

        public event EventHandler WorkCompleted;

        public Worker()
        {
            worker = new BackgroundWorker();
            worker.DoWork += new DoWorkEventHandler(OnWorkerDoWork);
            worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(OnWorkerRunWorkerCompleted);
        }

        private void OnWorkerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (WorkCompleted != null)
            {
                WorkCompleted(this, EventArgs.Empty);
            }
        }

        public void StartWork(Action work)
        {
            if (!IsBusy)
            {
                this.work = work;
                worker.RunWorkerAsync();
            }
            else
            {
                throw new InvalidOperationException("Worker is busy");
            }
        }

        private void OnWorkerDoWork(object sender, DoWorkEventArgs e)
        {
            work.Invoke();
            work = null;
        }
    }
}

这只是一个起点。

您可以使用一系列操作启动它,然后在完成该组操作后完成一个事件。

然后至少你可以使用ManualResetEvent来等待已完成的事件......或者你想要的任何逻辑。