C# 如何在列表框从其他线程更新后强制主GUI线程更新列表框?
我正在修补一个多线程下载程序,使用生产者/消费者队列结构;下载部分工作正常,但我在保持GUI更新时遇到了问题 现在,我在表单上使用listbox控件来显示状态消息和下载进度的更新,最终我希望用progressbars替换它 首先是后面的Form1代码;表单只包含一个按钮和列表框:C# 如何在列表框从其他线程更新后强制主GUI线程更新列表框?,c#,winforms,multithreading,user-interface,C#,Winforms,Multithreading,User Interface,我正在修补一个多线程下载程序,使用生产者/消费者队列结构;下载部分工作正常,但我在保持GUI更新时遇到了问题 现在,我在表单上使用listbox控件来显示状态消息和下载进度的更新,最终我希望用progressbars替换它 首先是后面的Form1代码;表单只包含一个按钮和列表框: public partial class Form1 : Form { public Form1() { InitializeComponent(); } public
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public void SetProgressMessage(string message)
{
if (listboxProgressMessages.InvokeRequired)
{
listboxProgressMessages.Invoke(new MethodInvoker(delegate()
{ SetProgressMessage(message); }));
}
else
{
listboxProgressMessages.Items.Add(message);
listboxProgressMessages.Update();
}
}
private void buttonDownload_Click(object sender, EventArgs e)
{
SetProgressMessage("Enqueueing tasks");
using (TaskQueue q = new TaskQueue(4))
{
q.EnqueueTask("url");
q.EnqueueTask("url");
q.EnqueueTask("url");
q.EnqueueTask("url");
q.EnqueueTask("url");
q.EnqueueTask("url");
q.EnqueueTask("url");
q.EnqueueTask("url");
q.EnqueueTask("url");
q.EnqueueTask("url");
}
SetProgressMessage("All done!");
}
}
现在是生产者/消费者逻辑。消费者一点一点地下载文件,并且应该告诉GUI线程上的listbox进度如何;这是可行的,但是列表框直到全部完成才真正更新,消息也会出现在“全部完成”之后信息,这是不可取的
TaskQueue.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.IO;
using System.Net;
using System.Windows.Forms;
using System.Runtime.Remoting.Messaging;
namespace WinformProducerConsumer
{
public class TaskQueue : IDisposable
{
object queuelocker = new object();
Thread[] workers;
Queue<string> taskQ = new Queue<string>();
public TaskQueue(int workerCount)
{
workers = new Thread[workerCount];
for (int i = 0; i < workerCount; i++)
(workers[i] = new Thread(Consume)).Start();
}
public void Dispose()
{
foreach (Thread worker in workers) EnqueueTask(null);
foreach (Thread worker in workers) worker.Join();
}
public void EnqueueTask(string task)
{
lock (queuelocker)
{
taskQ.Enqueue(task);
Monitor.PulseAll(queuelocker);
}
}
void Consume()
{
while (true)
{
string task;
Random random = new Random(1);
lock (queuelocker)
{
while (taskQ.Count == 0) Monitor.Wait(queuelocker);
task = taskQ.Dequeue();
}
if (task == null) return;
try
{
Uri url = new Uri(task);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
response.Close();
Int64 iSize = response.ContentLength;
Int64 iRunningByteTotal = 0;
using (WebClient client = new System.Net.WebClient())
{
using (Stream streamRemote = client.OpenRead(new Uri(task)))
{
using (Stream streamLocal = new FileStream(@"images\" + Path.GetFileName(task), FileMode.Create, FileAccess.Write, FileShare.None))
{
int iByteSize = 0;
byte[] byteBuffer = new byte[iSize];
while ((iByteSize = streamRemote.Read(byteBuffer, 0, byteBuffer.Length)) > 0)
{
streamLocal.Write(byteBuffer, 0, iByteSize);
iRunningByteTotal += iByteSize;
double dIndex = (double)iRunningByteTotal;
double dTotal = (double)byteBuffer.Length;
double dProgressPercentage = (dIndex / dTotal);
int iProgressPercentage = (int)(dProgressPercentage * 100);
string message = String.Format("Thread: {0} Done: {1}% File: {2}",
Thread.CurrentThread.ManagedThreadId,
iProgressPercentage,
task);
Form1 frm1 = (Form1)FindOpenForm(typeof(Form1));
frm1.BeginInvoke(new MethodInvoker(delegate()
{
frm1.SetProgressMessage(message);
}));
}
streamLocal.Close();
}
streamRemote.Close();
}
}
}
catch (Exception ex)
{
// Generate message for user
}
}
}
private static Form FindOpenForm(Type typ)
{
for (int i1 = 0; i1 < Application.OpenForms.Count; i1++)
{
if (!Application.OpenForms[i1].IsDisposed && (Application.OpenForms[i1].GetType() == typ))
{
return Application.OpenForms[i1];
}
}
return null;
}
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用系统文本;
使用系统线程;
使用System.IO;
Net系统;
使用System.Windows.Forms;
使用System.Runtime.Remoting.Messaging;
命名空间WinformProducerConsumer
{
公共类任务队列:IDisposable
{
object queuelocker=新对象();
工作人员;
Queue taskQ=新队列();
公共任务队列(int workerCount)
{
工人=新线程[工人计数];
对于(int i=0;i0)
{
streamLocal.Write(byteBuffer,0,iByteSize);
iRunningByteTotal+=iByteSize;
double dIndex=(double)iRunningByteTotal;
double dTotal=(double)byteBuffer.Length;
double dProgressPercentage=(dIndex/dTotal);
int iProgressPercentage=(int)(dProgressPercentage*100);
string message=string.Format(“线程:{0}完成:{1}%文件:{2}”,
Thread.CurrentThread.ManagedThreadId,
iProgressPercentage,
任务);
Form1 frm1=(Form1)FindOpenForm(typeof(Form1));
frm1.BeginInvoke(新方法调用程序(委托)()
{
frm1.SetProgressMessage(消息);
}));
}
streamLocal.Close();
}
streamRemote.Close();
}
}
}
捕获(例外情况除外)
{
//为用户生成消息
}
}
}
私有静态表单FindOpenForm(类型typ)
{
for(int i1=0;i1
}
有什么建议、例子吗?我四处寻找解决方案,但找不到任何可以遵循或有效的方法
将frm1.BeginInvoke(new MethodInvoker(delegate())替换为frm1.Invoke(new MethodInvoker(delegate())会导致死锁。我在这里很困惑
资料来源:
生产者/消费者示例:
更新:我的做法是错误的;我将使用GUI线程必须关注的事件,而不是从工作线程调用回GUI。吸取的教训是:我的另一个答案更合适。一旦更改TaskQueue以引发ProgressChanged事件,这个答案就更合适了
尝试调用
listboxProgressMessages.Refresh()
。这将强制绘制。请检查控件。Refresh
文档。有时,您必须调用表单的Refresh方法
Control.Refresh方法
强制控件使其属性无效
客户区域并立即重新绘制
自身和任何子控件
也在您的代码中