我在使用生產者/消費者隊列結構的多線程下載器上修修補補;下載部分工作正常,但我遇到了一個問題,保持GUI更新。如何強制主GUI線程在更新後從其他線程更新列表框?
現在我使用窗體上的列表框控件來顯示狀態消息和下載進度的更新,最終我希望用progressbar來替換它。
首先將Form1代碼放在後面;該表格包含只是一個按鈕,列表框:
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線程上的列表框如何進展;這是有效的,但是在完成所有操作之前,列表框並沒有實際更新,還有消息出現在「全部完成」之後。消息,這是不可取的。
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;
}
}
}
任何建議,例子嗎?我四處尋找解決方案,但找不到任何我可以遵循或工作的東西。 。
更換frm1.BeginInvoke(新MethodInvoker(委託()與frm1.Invoke(新MethodInvoker(委託()死鎖結果,我願意在這裏難倒
來源: 生產者/消費者例如:http://www.albahari.com/threading/part4.aspx
更新:我以錯誤的方式解決問題;而不是從工作線程調回GUI,我將使用GUI線程必須留意的事件。經驗教訓:)
好吧,我會在這個方向上尋找解決方案。 :) – MartijnK 2010-07-13 12:44:34
解決方案非常簡單。僞代碼。公共事件progresschange作爲事件處理程序。刪除findopenform,替換爲raise progresschange。在表單中,addhandler q.progresschange,address_reportprogresschange的地址。在form_reportprogresschange中調用SetProgressMessage。 – AMissico 2010-07-13 12:48:00
我很晚了,但通過這個工作。這是一個很好的編程練習,也是一個有趣的.NET技巧。 – AMissico 2010-07-13 12:51:54