使用invoke或后台工作程序更新UI

时间:2013-11-06 13:52:23

标签: c# multithreading

我在一个学校项目上工作,我需要做一些温度测量。任务是随机创建一些温度,然后计算平均值。我的代码如下,但我遇到了Threads的问题。在创建窗口句柄之前,不能将其称为对象。我在网上搜索,发现后台工作者对更新UI更有用。我还不熟悉编程,因为我刚开始上学。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Collections;
using System.IO;
using System.Threading;

namespace Temperatur
{
    public partial class Form1 : Form
    {
        static Random rnd = new Random();
        static ArrayList tempList = new ArrayList();
        static SemaphoreSlim w, e, b;

        public Form1()
        {
            InitializeComponent();

            w = new SemaphoreSlim(1);
            e = new SemaphoreSlim(0);
            b = new SemaphoreSlim(6);

            Thread t1 = new Thread(randomNr);
            t1.Start();

            Thread t2 = new Thread(gennemsnit);
            t2.Start();
        }

        public void randomNr()
        {
            //Thread.Sleep(100);
            for (int i = 0; i < 10; i++)
            {
                //b.Wait();
                //w.Wait();
                int number = rnd.Next(36, 42);
                tempList.Add(number);

                listBox1.BeginInvoke((MethodInvoker)delegate
                {
                    listBox1.Items.Add(number);
                });
                //w.Release();
                //e.Release();
            }
        }

        public void gennemsnit()
        {
            double avg = 0;
            double nb = 0;
            //Thread.Sleep(200);

            for (int i = 0; i < tempList.Count; i++) //i < tempList.Count
            {
                //e.Wait();
                //w.Wait();
                nb += Convert.ToDouble(tempList[i]);
                avg = nb / tempList.Count;

                listBox2.Invoke((MethodInvoker)delegate //listbox2.invoke
                {
                    listBox2.Items.Add(avg);
                });
                //w.Release();
                //b.Release();
            }
        }
    }
}

3 个答案:

答案 0 :(得分:0)

BackgroundWorker确实是您所需要的。如果“报告进度”,则可以将对象传递到进度报告中的GUI线程。像这样设置进度报告:

    BackgroundWorker bw = new BackgroundWorker();

...

        bw.WorkerReportsProgress = true;
        bw.DoWork += bw_DoWork;
        bw.ProgressChanged += bw_ProgressChanged;

...

        void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    // Here you have passed yourself any object you like. Could be your own class. Could be a string, etc.
    MyClass myObject = e.UserState as MyClass;


    // Then you can add it to your GUI as necessary, for example
    listbox2.Items.Add(myObject);    
}

您可能只想传递一个字符串作为对象,然后将该字符串添加到列表框中。

答案 1 :(得分:0)

我不确定您的代码尝试做什么,但您的任务非常简单。你真的不必担心线程。为简单起见,您可以安排计时器运行每个1s并选择一个随机数/读取温度并在UI上更新它。如果从窗体控件(System.Windows.Forms.Timer)中选择计时器,则可以直接从触发时触发的函数更新UI。如果您使用System.Timers.Timer中的计时器,则应使用BeginInvoke更新列表。

答案 2 :(得分:0)

  

在创建窗口句柄之前,不能将其称为对象。

不要在Form的构造函数中启动线程。请改为使用Load()或Shown()事件:

    public Form1()
    {
        InitializeComponent();

        w = new SemaphoreSlim(1);
        e = new SemaphoreSlim(0);
        b = new SemaphoreSlim(6);

        this.Load += new EventHandler(Form1_Load);
    }

    void Form1_Load(object sender, EventArgs e)
    {
        Thread t1 = new Thread(randomNr);
        t1.Start();

        Thread t2 = new Thread(gennemsnit);
        t2.Start();
    }