使用线程C#

时间:2011-06-12 07:17:33

标签: c# winforms multithreading listbox parallel-processing

我需要一些帮助,试图弄清楚我做错了什么。我正在尝试从单独的线程中获取系统日志中的项集合,以防止表单在收集过程中被冻结。我可以让后台工作者全部抓住它们,但我有一些问题将它们添加到表单上的ListBox

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{

  foreach (System.Diagnostics.EventLogEntry entry in eventLog1.Entries)
  {
     listBox1.Items.Add(
        entry.EntryType.ToString() + " - " + 
        entry.TimeWritten + "     - " + 
        entry.Source);
  }
}

显然这没有按预期工作,因为有两个独立的线程,你不能像我发现的那样更改不同线程上的对象。所以,如果有人能指导我朝着正确的方向前进,我会很感激。

5 个答案:

答案 0 :(得分:4)

您不应该从非UI线程访问UI元素。运行ReportProgress,它将与UI线程同步。

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    foreach (System.Diagnostics.EventLogEntry entry in eventLog1.Entries)
    {
        var newEntry = entry.EntryType + " - " + entry.TimeWritten + "     - " + entry.Source;
        backgroundWorker1.ReportProgress(0, newEntry);
    }
}

void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    var newEntry = (string)e.UserState;
    listBox1.Items.Add(newEntry);
}

确保启用WorkerReportsProgress

backgroundWorker1.WorkerReportsProgress = true;

并订阅了ProgressChanged

backgroundWorker1.ProgressChanged += backgroundWorker1_ProgressChanged;

另一种方法是在

中调用Control.Invoke
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    foreach (System.Diagnostics.EventLogEntry entry in eventLog1.Entries)
    {
        var newEntry = entry.EntryType.ToString() + " - " + entry.TimeWritten + "     - " + entry.Source;
        Action action = () => listBox1.Items.Add(newEntry);
        Invoke(action);
    }
}

但是使用这种方法,您不需要BackgroundWorker,因为它的重点是使用与UI线程同步的ProgressChangedRunWorkerCompleted事件处理程序。

答案 1 :(得分:1)

您需要使用后台工作程序的报告进度选项。 google this

答案 2 :(得分:1)

使用BackgroundWorker时不应出现任何问题。对回调方法的所有调用都在相同的UI上下文中运行。

修改

如果要报告进度,则需要将SynchronizationContext.Current存储到最佳启动状态。或者您可以使用IsInvokeRequired模式。以下是我使用SynchronizationContext

的方法
 private SynchronizationContext uiContext;
        public Form1()
        {
            uiContext = SynchronizationContext.Current;
            InitializeComponent();
            FillItem();
        }

我有以下代码,它就像魅力一样。

    public void FillItem()
            {
                BackgroundWorker worker = new BackgroundWorker();
                worker.WorkerReportsProgress = true;
                worker.DoWork += (a, b) =>
                                     {
                                         int i = 0; //Percentage complete, roll your own logic.
                                         foreach (var eventLog in EventLog.GetEventLogs())
                                         {
                                             foreach (EventLogEntry entry in eventLog.Entries)
                                             {
                                                 this.listBox1.Items.Add(entry.Message);
 uiContext.Post(z=>worker.ReportProgress(i++),null);

                                             }
                                         }


                                     };
                worker.RunWorkerAsync();
                worker.ProgressChanged += (a, b) => this.progressBar1.Value = b.ProgressPercentage;


            }

答案 3 :(得分:1)

尝试这个,在Control的线程上调用动作的非常简单的方法:

private void Form1_Load(object sender, EventArgs e)
{
    var bw = new BackgroundWorker();
    bw.DoWork += DoWork;
    bw.RunWorkerAsync();
}
private void DoWork(object sender, DoWorkEventArgs e)
{
    var itemList = new List<int> {1, 22, 3, 4};
    var func = new Action<int>(itemToAdd => listBox1.Items.Add(itemToAdd));
    foreach (var item in itemList)
    {
        listBox1.Invoke(func, item);
    }
}

答案 4 :(得分:0)

只允许GUI线程修改GUI元素。如果您不遵守此规则,您将获得例外。你可以:

  1. 使用MethodInvoker从GUI线程中触发调用
  2. 使用报告进度功能(实际上为1.)。
  3. 将对象存储在另一个数据结构(锁定)中,并使用GUI线程中的计时器来拉取对象并显示它们。