gpt4 book ai didi

c# - 从另一个线程更新列表框后,如何强制主GUI线程更新列表框?

转载 作者:行者123 更新时间:2023-12-03 13:22:22 31 4
gpt4 key购买 nike

我正在使用生产者/消费者队列构造来修补多线程下载器。下载部分工作正常,但是在更新GUI时遇到了问题。

现在,我在窗体上使用列表框控件来显示状态消息和下载进度的更新,最终我希望将其替换为进度条。

首先是后面的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(new MethodInvoker(delegate()替换为frm1.Invoke(new MethodInvoker(delegate()会导致死锁)。我在这里很困惑。

资料来源:
生产者/消费者示例: http://www.albahari.com/threading/part4.aspx

更新:我将以错误的方式进行操作;而不是从工作线程调用回GUI,我将使用GUI线程必须注意的事件。一个教训。 :)

最佳答案

您应该消除 FindOpenForm并将ProgressChanged事件添加到TaskQueue。直接调用表示层(窗体或控件)是绝对不是 TaskQueue的责任。然后,表单的职责是监听任务生成的“进度更改”事件,然后适本地更新自身。

这将轻松解决您的问题,将保持简单,遵循最佳实践,并消除时序问题,线程问题等。

  • MSDN在http://social.msdn.microsoft.com/Search/en-US?query=raising+events&ac=8上搜索引发事件
  • http://msdn.microsoft.com/en-us/library/aa645739(VS.71).aspx上的
  • 事件教程(C#)
  • http://msdn.microsoft.com/en-us/library/ms973905.aspx上引发事件并响应事件(VB.NET)
  • [必读] BackgroundWorker组件概述http://msdn.microsoft.com/en-us/library/8xs8549b.aspx上的说明了您需要完成的TaskQueue事件以及表单对这些事件的处理。
  • 关于c# - 从另一个线程更新列表框后,如何强制主GUI线程更新列表框?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3236948/

    31 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com