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