繁体   English   中英

C#多线程多类GUI应用程序,我这样做对吗?

[英]C# Multithread multiclass gui app, am I doing this right?

我创建了以下项目,向大家展示我打算如何做事。 但是我的主要项目将更大,并且将有多个班级。 我只是想让它正常工作,所以我知道我在编码时正在使用良好的做法。

好的,让我们开始:),所以我的表单有一个名为“ button1”的按钮和一个名为“ textBox1”的文本框,并且我有一个名为“ Class1”的类,其中有一个方法为“ testtest()”,我只是将Thread.Sleep在testtest方法中,这样我就可以发现它在另一个线程上运行。

这是我的表格代码

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

namespace Test
{
  public partial class Form1 : Form
  {
    public Form1()
    {
      InitializeComponent();
    }

    delegate void Class1Deligate();

    private void button1_Click(object sender, EventArgs e)
    {
      Class1 c = new Class1();
      Class1Deligate testMethod = new Class1Deligate(c.testtest);
      testMethod.BeginInvoke(endTestMethod, testMethod);
    }

    void endTestMethod(IAsyncResult ar)
    {

    }
  }
}

这是我的第一课代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace Test
{
  class Class1
  {
    public void testtest()
    {
      Thread.Sleep(8888);
      MessageBox.Show("Done");
    }
  }
}

我是否正确创建新线程? 有人可以告诉我在运行时如何从class1中的testtest方法更新textbox1吗? 我发表了较早的文章,并告诉我使用Dispatcher,但是由于某种原因,看来Dispatcher类对我不可用。

最好的祝福

我不认为您实际上是在开始一个线程。 delegate.BeginInvoke可能使用内部ThreadPool进行投标。 另外,我不认为您应该在没有适当的EndInvoke调用之后调用BeginInvoke (否则,将不会释放从BeginInvoke调用返回的对IAsyncResult的引用)。

如果您想要一个新线程,则可以使用Thread类创建一个新线程

Thread t = new Thread(c.testtest);
t.IsBackground = true; // end if main thread ends
t.Start();

请注意,如果您使用上述的Thread类并使用GUI,则需要在GUI线程上附加一个方法来更新它。 您可以使用Control.Invoke调用来实现

public void updateGUI(object state) {
    if (control.InvokeRequired) {
        control.Invoke(new Action<object>(updateGUI), state);
        return;
    }
    // if we are here, we can safely update the GUI
}

首先,如果您愿意,可以使用ThreadPool类进行线程化。 我通常用它来触发新线程,例如,

ThreadPool.QueueUserWorkItem(f.ThreadPoolCallback, i);

该链接进一步说明了它们。 对于GUI,您需要检查当前线程是否与UI线程相同(因为只有UI线程才能更新UI),如果不是,请在UI线程上调用一个函数。 UI线程。 可以这样做

delegate void ChangeFormUnsafeCallback(string text);
private void ChangeFormUnsafe(string text)
{
  // Do stuff to form/controls
}

private void SomeFunction()
{      
  myForm.Invoke(new ChangeFormUnsafeCallback(ChangeFormUnsafe), "foo");
}

您可以进行检查以查看是否需要调用,但是通常很简单地知道该函数是否将在UI线程内使用并适当地调用。 根据一般经验,除非您的函数是由于表单上发生了某些事情(例如,单击按钮)而运行的,否则您将需要使用Invoke函数。

阿里亚

使用您的方法(异步委托),您没有启动新线程,而是使用了.net线程池中的线程,该线程将在您完成工作时释放。 调用委托并在完成后被回调的正确方法如下:

    void button1_Click(object sender, EventArgs e)
    {
        Class1 c = new Class1();
        Action action = c.Test;

        action.BeginInvoke(new AsyncCallback(EndTestMethod), null);
    }
    void EndTestMethod(IAsyncResult token)
    {    
        Action callBack = (Action)((AsyncResult)token).AsyncDelegate;
        callBack.EndInvoke(token);
    }

如果您的方法寿命很短并且需要回调,则异步委托方法很有用。 否则,对于更长的运行过程,您可能需要创建并启动一个新线程。

 Thread t = new Thread(c.Test);
        t.IsBackground = true;
        t.Start();

但是,通过回调发回调用线程的信号将变得冗长,因为您必须自己编写此代码。

关于Dispatcher,这是一个WPF类,用于排队由WPF应用程序中的UI线程处理的操作(以及其他操作)。 从您的代码看来,您正在编写Winforms应用程序,因此此类型不合适。

最后,为了更新文本框,您需要检查是否正在尝试在创建控件(UI线程)的同一线程上访问该控件。 这是通过control.invokerequired和control.invoke完成的。 如下:

       void AddText(string text)
    {
        if (textBox1.InvokeRequired)
        {
            textBox1.Invoke((Action<string>)AddText, text);
        }
        textBox1.Text = text;
    }

根据您要完成的工作,可以采取几种不同的方法。 如果您要执行单个任务而又不阻塞UI线程,则可以使用BackgroundWorker 这将为您提供一种非常简单的方式来保持UI活泼,并使UI更新变得容易,而无需调用等。

如果要执行很多任务,则可能应该使用ThreadPool 如果您需要为线程分配优先级,否则它们将花费大量时间进行阻塞,那么您应该创建自己的实例线程。

您当然可以继续使用ThreadPool来做您自己的事情,但是还有其他一些方法可能会更好地满足您的特定需求。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM