当我使用交叉线程,然后我的应用程序不响应,直到后台操作完成

本文关键字:不响应 后台 操作 应用程序 然后 线程 我的 | 更新日期: 2023-09-27 18:13:36

我正在从。net framework 2.0应用程序中工作。

有一些操作在后台运行,如数据库备份,进度条和标签文本更新等。但是当我使用交叉线程时,我的应用程序不响应(繁忙图标),直到后台操作完成

这是示例代码

        private void button1_Click(object sender, EventArgs e)
        {
            Thread t = new Thread(new ThreadStart(UpdateInfo));
            t.Start();
        }

        private void UpdateInfo()
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new MethodInvoker(UpdateInfo)); 
            }
            else
            { 
                // send query to database here for taking backup that could take time
                // update progress bar
                //I'm also using sqlconnection InfoMessage here
                label1.Text = "Text upading......
            }
        }
         private void OnInfoMessage(sender As Object, e As SqlInfoMessageEventArgs)
         {
         }

场景:场景:用户可以取消操作,但由于应用程序没有响应而无法取消

================ 更新代码 ==========================================

My Code就像

private void btnBackup_Click(object sender, EventArgs e)
{
  Thread t = new Thread(new ThreadStart(MyThreadFunc));
  t.Start();
}

public void MyThreadFunc()
{
    if (this.InvokeRequired) {
        this.Invoke(new MethodInvoker(Backup));
    } else {
        Backup();
    }
}
public void Backup()
{
    string databaseName = cbDatabase.Text;// getting the name of database for backup

    SaveFileDialog1.ShowDialog(); // dialog will open   
    string backupFileName = SaveFileDialog1.FileName; // getting location of backup

    //============ database query==================
    SqlConnection con = new SqlConnection(conString);
    con.FireInfoMessageEventOnUserErrors = true;
    con.InfoMessage += OnInfoMessage;
    con.Open();
    query = string.Format("backup database {0} to disk = {1}", databaseName,backupFileName);
    using (cmd == new SqlCommand(query, con)) {
        cmd.CommandTimeout = 0;
        cmd.ExecuteNonQuery();
    }
    con.Close();
    con.InfoMessage -= OnInfoMessage;
    con.FireInfoMessageEventOnUserErrors = false;
    //============ Database operation end==================
}
private void OnInfoMessage(object sender, SqlInfoMessageEventArgs e)
{
    lblStatusMsg.Text = e.Message; // mostly messages are like. 1 percent complete, 5 percent complete, 11 percent complete

    foreach (SqlError info in e.Errors) {
        if (info.Class > 10) {
                           // errror logging
        } else {
            Regex reger = new Regex("''d+");
            Match regerMatch = reger.Match(e.Message);
            if (ProgressBar1.Value == 100) {
            } else {
                ProgressBar1.Value = regerMatch.Value;
            }
        }
    }
}

数据库操作完成后才响应

当我使用交叉线程,然后我的应用程序不响应,直到后台操作完成

Invoke调用的目的是让代码在主线程上运行。因此,您的代码创建了一个线程,其全部目的是强制主线程运行所有代码。

假设您想要运行一个线程,它在启动后10秒更新标签的文本以指示完成。您仍然需要Invoke标签更新,但这是调用中唯一应该做的事情。

在这种情况下,你的线程函数应该看起来像这样:
private void MyThreadFunc()
{
    // do something here
    Thread.Sleep(10000);
    // update the label:
    if (label1.InvokeRequired)
        Invoke(UpdateLabel);
    else
        UpdateLabel();
}
private void UpdateLabel()
{
    label1.Text = "Something was finished.";
}

换句话说,你需要分离出那些在主线程上运行的东西(像任何更新你的窗体上的控件)和Invoke只有那些位。其余的应该发生在Invoke之外。


我想我没有说清楚。

Invoke方法用于在拥有你正在调用的控件或窗体句柄的线程上下文中执行代码。您可以使用它来与UI上的控件进行交互,但是您应该仅将用于此目的。如果你把所有的线程关闭在Invoke调用中,那么所有线程的代码将在UI线程中运行,这使得拥有一个单独的线程完全没有意义。

如果您想阻止应用程序的UI在事情发生时暂停——毕竟,这是使用线程的主要原因之一——那么您应该只在绝对必要的时候使用Invoke方法,并且只用于非常小的代码段。调用Invoke来更新控件的参数,与窗体的非线程安全属性交互,等等。您可以直接从其他线程使用对话框等,尽管有些人更喜欢使用Invoke

如果你正在做多个调用,那么你可能应该写一些辅助方法来包装Invoke来清理东西。比如:

public void Invoker(Action action)
{
    if (InvokeRequired)
        Invoke(action);
    else
        action();
}
public T Invoker<T>(Func<T> func)
{
    if (InvokeRequired)
        return (T)Invoke(func);
    else
        return func();
}

现在,您可以像这样以最小的影响编写线程代码:

public void ThreadFunc()
{
    System.Threading.Thread.Sleep(1000);
    Invoker(() => this.label1.Text = "Started");
    for (int i = 1; i < 10; i++)
    {
        System.Threading.Thread.Sleep(1000);
        Invoker(() => this.label1.Text = string.Format("Iteration {0}", i));
    }
    System.Threading.Thread.Sleep(1000);
    Invoker(() => this.label1.Text = "Completed");
}

或者如果你不喜欢lambda函数(出于某种原因),你可以使用这样的方法:

public void Invoker<T>(Action<T> action, T p)
{
    if (InvokeRequired)
        Invoke(action, p);
    else
        action(p);
}
private void SetLabel(string value)
{
    label1.Text = value;
}

然后在你的代码中:

Invoker(SetLabel, "new text value");

重要的是要保持你调用的代码很小,否则你最终会阻塞主线程。