多线程暂停形成

时间:2013-12-05 11:58:17

标签: c# multithreading winforms

我正在做一些MultiThreading但是表格在加载时暂停。

我正在尝试显示表单,然后在后台它应该填充组合框而不会暂停表单。

在我的Form_Load事件中,我有这个:

private void frmIni_Load(object sender, EventArgs e)
{
      Application.DoEvents();
      Thread threadOne = new Thread(GetServers);
      threadOne.Start();
}

在我的GetServers()方法中:

private void GetServers()
{
       cboServer.BeginInvoke(
          (Action)(() => {
              servers = SmoApplication.EnumAvailableSqlServers(false);
              Thread.Sleep(1);
              foreach (DataRow server in servers.Rows)
              {
                  cboServer.Properties.Items.Add(server["Name"]);
                  Thread.Sleep(1);
              }
        }));
}

我在这里缺少什么?表单不应该暂停,它应该工作,然后最终当线程完成时,它应该只填充组合框。

5 个答案:

答案 0 :(得分:6)

是的,因此它阻止UI的原因很简单,因为在新线程中没有运行真正的代码。在你对GetServers的调用中,你回调到UI线程,然后做忙碌的事情(你可能根本不在这里使用线程......)。

你想在线程中放置任何长时间运行的工作,并且当你想要更新它时只回调到UI线程,例如。

private void frmIni_Load(object sender, EventArgs e)
{
    Task.Factory.StartNew(() => {
        return SmoApplication.EnumAvailableServers(false);
    }).ContinueWith((task) => {
        foreach (var server in task.Result)
        {
            cboServer.Properties.Items.Add(server["Name"]);
        }
    }, TaskScheduler.FromCurrentSynchronizationContext());
}

注意事项

  • 尝试多线程时不要使用DoEvents,尤其是当您不理解usage时。{/ li>
  • 除非您确定需要(例如,长时间运行的I / O任务)
  • ,否则不要启动新线程
  • 请勿使用Thread.Sleep来模拟“暂停”(您似乎正在进行此操作)
  • 使用线程池进行短期工作(如下所示)。

答案 1 :(得分:1)

由于BeginInvoke,所有代码都在UI线程上执行。

private void GetServers()
{
    servers = SmoApplication.EnumAvailableSqlServers(false);
    Thread.Sleep(1); // Why?
    cboServer.BeginInvoke(
            (Action)(() => {
                foreach (DataRow server in servers.Rows)
                {
                    cboServer.Properties.Items.Add(server["Name"]);
                    Thread.Sleep(1); // Why?
                }
            })
        );
}

答案 2 :(得分:1)

首先:创建一个Thread将消耗少量时间。此外,您将使用BeginInvoke

将实际工作发送回用户界面
cboServer.BeginInvoke(
    (Action)(() => {
        servers = SmoApplication.EnumAvailableSqlServers(false);
        Thread.Sleep(1);
        foreach (DataRow server in servers.Rows)
        {
            cboServer.Properties.Items.Add(server["Name"]);
            Thread.Sleep(1);
        }
    })
);

您应该尝试将除UI更新之外的所有内容移出BeginInvoke委托,不知何故如下:

private void GetServers()
{
       servers = SmoApplication.EnumAvailableSqlServers(false);

       cboServer.BeginInvoke(
          (Action)(() => {
              foreach (DataRow server in servers.Rows)
              {
                  cboServer.Properties.Items.Add(server["Name"]);
              }
        }));
}

答案 3 :(得分:0)

这是针对您的场景/情况执行此操作的正确方法。

你应该在线程池上创建一个线程并让它进行线程管理。此外,当您的线程被池踢出/调用时,一旦获得所有SQL Server,您就可以通过开始调用添加到组合框中,这样您就可以在UI线程和当前线程之间正确切换以更新组合框。

Threadpool:

http://msdn.microsoft.com/en-us/library/vstudio/system.threading.threadpool.queueuserworkitem(v=vs.100).aspx

http://msdn.microsoft.com/en-us/library/vstudio/kbf0f1ct(v=vs.100).aspx

在游泳池调用的方法中,通过执行begininvoke将项目添加到组合框中。

更新 - 我看到@James几乎发布了我的建议。伟大的思想相似。

答案 4 :(得分:0)

尽管答案很严重,但您的代码仍然无法按预期工作:

private void GetServers()
{
    servers = SmoApplication.EnumAvailableSqlServers(false);
    // Thread.Sleep(1);
    foreach (DataRow server in servers.Rows)
    {
          cboServer.BeginInvoke((Action)(() => { cboServer.Properties.Items.Add(server["Name"]);}));
        // Thread.Sleep(1);
    }
}