【问题标题】:Aborted thread can not be started a second time中止的线程无法再次启动
【发布时间】:2015-03-18 10:13:29
【问题描述】:

我有一个问题,不知道如何解决。我要开新帖了:

private void button_Click(object sender, EventArgs e)
{
        Thread thrd = new Thread(new ThreadStart(loadingScreenStart));
        thrd.Start();

        //setting some variables, entering some methods etc...

        thrd.Abort();
}

public void loadingScreenStart()
{
        splashScreen splashObj = splashScreen.GetInstance();
        Application.Run(splashScreen.GetInstance());
}

我有另一种形式:

private static splashScreen m_instance = null;
private static object m_instanceLock = new object();

public static splashScreen GetInstance()
{
        lock (m_instanceLock)
        {
            if (m_instance == null)
            {
                m_instance = new splashScreen();
            }
        }
        return m_instance;
}

这很好,但是当我第二次点击按钮时,我得到一个异常,即无法访问丢弃的对象。为什么以及如何解决这个问题?我的意思是在线程中止后,我再次点击按钮时会创建一个新线程。

【问题讨论】:

  • 我认为你有问题对象不是线程stackoverflow.com/questions/6993407/…
  • 你为什么要中止线程?通常应该避免中止线程。只要让线程函数完成执行,它就会消失。关于您的问题,启动画面在执行后永远不会设置为 null,因此句柄被丢弃,您返回它的丢弃实例。
  • 我需要中止它。否则它不会关闭(它显示一个加载屏幕)并且我得到错误的值显示
  • @Takeda15 - 中止线程会使您的程序处于未定义状态。只有在强制程序关闭时才应该这样做。
  • 这里的主要问题是他在 UI 线程上做繁重的工作,同时试图在辅助 UI 线程上显示启动屏幕。如果把繁重的工作转移到另一个线程上会好很多。

标签: c# multithreading winforms


【解决方案1】:

被丢弃的不是线程,而是splashScreen 实例。您应该只创建一个新的,而不是尝试重复使用旧的。

【讨论】:

  • 我现在用“splashScreen splashObj = splashScreen.GetInstance();”试了一下在 Application.Run 方法之前,但同样的错误。我需要使用闪屏表单的一些控件,所以 GetInstance 方法是唯一的方法。我不能销毁对象吗?
  • @Takeda15 你知道你的代码是做什么的吗?无论您调用多少次splashScreen.GetInstance(),它都会总是返回相同的splashScreen 实例。这就是单例的全部意义所在,但是当您 Dispose 单例时它显然不起作用。
【解决方案2】:
Thread thrd = new Thread(new ThreadStart(loadingScreenStart));
thrd.Start();

//setting some variables, entering some methods etc...

thrd.Abort();

为什么要调用 thrd.Abort()?你知道线程完成吗? 您必须等待线程完成。 并在这里使用双重检查

public static splashScreen GetInstance()
{
        if (m_instance == null)
        {
            lock (m_instanceLock)
            {
                if (m_instance == null)
                {
                   m_instance = new splashScreen();
                }
            }
        }
        return m_instance;
}

也许当你调用 Run splashscreen 时,它被处理掉了。尝试创建并将其捕获到一个字段并将其传递给您的方法。试试这个。

Task.Run(() => 
        var splashObj = splashScreen.GetInstance();
        Application.Run(splashObj);
    }));

【讨论】:

  • 也许你只是 var splashObj = new splashScreen();确保你的班级没有被处置。你看到异常了吗?有什么例外吗?更多细节。
  • 不,这不起作用。他不认识“var”,括号有问题
  • 是否可以检测线程是否在后台运行?不知何故,我什至无法访问 form1.thrd
【解决方案3】:

好的,我以某种方式解决了它。不要认为这是一个好的解决方案,但它确实有效。我没有中止线程,而是隐藏 SplashScreen 的表单并检查线程是否已经在运行。如果是,那么我只是显示表格。如果没有,我创建一个新实例。

【讨论】:

  • 你就不能Form.Close()吗?
【解决方案4】:

这过于复杂且不安全。如果您真的想使用“启动画面”来处理这个问题,为什么不尝试这样的事情呢?

using (var splashScreenForm = SplashScreen.ShowSplashScreen())
{
  // Do your work
}

SplashScreen 有这样的方法:

public static SplashScreen ShowSplashScreen()
{
  var form = new SplashScreen();

  new Thread(() => Application.Run(form)).Start();

  return form;
}

public override void Dispose(bool disposing)
{
  if (disposing)
  {
    if (InvokeRequired)
    {
      Invoke(() => base.Dispose(true));

      return;
    }

    base.Dispose(true);
  }
  else base.Dispose(disposing);
}

【讨论】:

    【解决方案5】:

    在重新审视整个事情之后,我意识到这里还有另一个问题,这是完全错误的处理方向。

    在您的button_Click 活动中,您显然做了很多需要花费大量时间的复杂事情。否则,您不需要显示启动画面。然而,在事件处理程序中这样做本身就是一个坏主意。您完全锁定了 UI 线程,Windows 很快就会认为该窗口被“挂起”。它甚至不能重绘自己!

    所以你应该从完全相反的方向来处理这个问题。在使用 UI 线程进行繁重的工作时,不要尝试从另一个线程显示启动画面,只需将繁重的工作转移到另一个线程!然后启动屏幕不需要像在另一个线程中调用Application.Start() 这样的奇特的东西。只需一个简单的.ShowModal() 就足够了。当另一个线程完成它的工作时,它可以在你的启动屏幕上调用.Close()

    在设计良好的应用程序中,几乎不需要第二个 UI 线程。需要将繁重的工作转移到其他线程,而不是 UI。

    请注意,如果您想从另一个线程操作 UI,您需要做一些 Invoke() 的事情。在这里阅读更多:https://msdn.microsoft.com/en-us/library/system.windows.forms.control.invokerequired(v=vs.110).aspx

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-01-18
      • 1970-01-01
      • 1970-01-01
      • 2018-01-19
      • 1970-01-01
      • 1970-01-01
      • 2020-10-04
      • 1970-01-01
      相关资源
      最近更新 更多