C# 如何实现线程队列/等待?

C# 如何实现线程队列/等待?,c#,multithreading,C#,Multithreading,我有一个计时器每15分钟调用一个函数,这个函数计算DGV中的行数,并为每行(另一个函数)启动一个线程,该线程解析一个网页,可能需要1秒到10秒的时间来完成 虽然它可以像处理1-6行一样正常工作,但如果再处理,则会导致请求超时 我希望它等待新创建的线程完成处理,然后再回到循环中创建另一个线程,而不锁定主UI for (int x = 0; x <= dataGridFollow.Rows.Count - 1; x++) {

我有一个计时器每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
                }

for(int x=0;x直接调用方法或执行while循环(使用睡眠调用)以检查线程的状态


也有异步事件,但会调用另一个方法,您希望从同一点继续。

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

  • 避免在具有不确定边界的循环中创建线程。创建线程时会有大量开销。如果操作数事先未知,请使用
    ThreadPool
  • 通过使用
    thread.Join
    阻止UI线程,您将无法获得所需的行为。这将导致UI变得无响应,并且它将有效地序列化操作,并抵消您希望通过线程获得的任何优势
如果您真的想限制并发操作的数量,那么更好的解决方案是创建一个单独的专用线程来启动操作。该线程将无限期地围绕一个循环旋转,等待项目出现在队列中,当它们出现时,它将使它们出列,并使用该信息异步启动操作PLY(再次使用
线程池
或TPL)。排队线程可以包含限制并发操作数的逻辑。搜索有关生产者-消费者模式的信息,以更好地了解如何实现此功能


有一个学习曲线,但谁说线程很容易,对吗?

考虑使用异步CTP。这是微软最近发布供下载的异步模式。它应该极大地简化异步编程。链接是。(首先阅读白皮书)

您的代码如下所示(对不起,我还没有验证我的语法)

private async Task DoTheWork()
{

对于(int x=0;x为了避免UI冻结,框架专门为以下目的提供了一个类:查看BackgroundWorker类(在单独的线程上执行操作),以下是一些信息:

顺便说一句,如果我理解正确,您不想并行化任何操作,因此只需等待解析页面的方法完成。基本上,对于网格的每一行(foreach外观),您都会获得id并调用该方法。如果您想并行化,只需重用相同的foreach循环并添加并使其并行


如果我理解正确,您当前所做的是在UI线程中的ID列表中循环,启动一个新线程来处理每个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。

您想要的是设置一些执行某些任务的工作程序

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

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

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;
        }
    }
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用系统组件模型;
使用系统线程;
命名空间工作测试
{
班级计划
{
静态void Main(字符串[]参数)
{
WorkerGroup WorkerGroup=新的WorkerGroup();
Console.WriteLine(“开始…”);
对于(int i=0;i<100;i++)
{
变量工作=新操作(()=>
{ 
线程。睡眠(1000);//一些工作
});
workerGroup.AddWork(工作);
}
while(workerGroup.WorkCount>0)
{
Console.WriteLine(workerGroup.WorkCount);
睡眠(1000);
}
控制台写入线(“Fin”);
Console.ReadLine();
}
}
公共类工作组
{
私人名单工人;

私有队列等待完成的事件..或任何您真正想要的逻辑。

如果是这种情况,为什么您使用新线程而不调用方法?您的for循环(如上所述)是否在UI线程上运行?如果您启动一个线程,然后立即阻止它完成,则启动该线程不会获得任何好处。Y你可以直接调用
UpdateLo()
直接,避免麻烦。感谢您的宝贵评论,康斯坦丁的话很有意义,但是for循环在UI线程上,因此调用方法会阻止UI…我希望我在这方面没有那么差。好的,我重新思考了我的过程,使用了method+BackgroundWorker,它工作得很好!感谢大家的支持NSWER,我真的很喜欢这个论坛和它的社区。
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;
        }
    }
}