【问题标题】:C# thread called multiple times but runs once [duplicate]C#线程多次调用但运行一次[重复]
【发布时间】:2013-04-15 03:24:18
【问题描述】:

基本上我有一个带有按钮的表单,当按下按钮时,它会创建一个运行线程的类的实例。当线程完成后,它会自动调用 Thread.Abort()。

我目前的代码归结为:

按钮:

private void Buttonclick(object sender, EventArgs e)
{
     MyClass c = new MyClass()
     c.Do_your_thing();
}

类:

public class MyClass
{
    Thread t;

    public void Do_your_thing()
    {
         t = new Thread(Running_code);
         t.Start();
    }

    private void Running_code()
    {
         //Perform code here
         t.Abort();
    }
}

当我单击一次按钮时,一切正常。但是当我再次按下按钮时,什么也没有发生。

当我不使用 t.Abort() 时,一切正常。但是不使用 t.Abort() 会导致内存泄漏并且程序无法正常关闭(线程永远不会关闭,因此进程将保持活动状态)。

谁能解释一下这是怎么回事?我该如何解决?

编辑:根据要求,我发布了一些实际代码

public class MyClass
{
    public void Test()
    {
        t = new Thread(() =>
            {
                wb.DocumentCompleted += get_part;
                wb.Navigate("http://www.google.com");
                Application.Run();
            });

        t.SetApartmentState(ApartmentState.STA);
        t.Start();
    }

    public void get_part(object sender, WebBrowserDocumentCompletedEventArgs e)
    {
        var br = sender as WebBrowser;
        string url = e.Url.ToString();

        //Here is some code that compares the url to surten predefined url. When there is a match, it should run some code and then go to a new url

        if(url == string_final_url)
        {
            //Finally at the url I want, open it in a new Internet Explorer Window
            Process proc = Process.Start("IExplore.exe", url);           
        }
    }
}

这是一个小型网络爬虫程序的一小部分。它导航到需要一些登录信息的网页。当我到达我真正想要的页面时,他应该在新的 Internet Explorer 中打开它。

当我调用此代码并关闭表单时,它仍然在进程树中可见。而且当我多次点击按钮时,使用的内存越来越高,我怀疑这是某种内存泄漏。

【问题讨论】:

  • 试试t.Join()而不是Abort
  • (旁白:永远不要打电话给Thread.Abort()。你会后悔的。)
  • @OP:请发布更多有代表性的代码。您发布的代码在没有t.Abort() 的情况下可以正常工作。
  • “但不使用 t.Abort() 会导致内存泄漏”——它不应该,除非你做错了什么。传递给new Thread() 的方法中的returning(或达到最终的})应该足以让该线程终止。
  • 这里有一本关于线程的优秀电子书:AlbahariForeground and Background Threads 有一个部分,大约在第一页的一半。

标签: c# multithreading abort


【解决方案1】:

首先,永远不要使用Thread.Abort()。有关原因的更多详细信息,请参阅Is this thread.abort() normal and safe?

Therearemanywarningsall overthe net关于使用Thread.Abort()。除非真的需要,否则我建议避免使用它,在这种情况下,我认为不需要。你最好只实现一个一次性计时器,可能有半秒的超时,并在每次击键时重置它。这样,您的昂贵操作只会在用户不活动半秒或更长时间(或您选择的任何长度)后发生。

您可以使用Join() Method,而不是使用中止。此方法阻塞调用线程,直到线程终止。

它使用的一个例子是

Thread t1 = new Thread(() => 
{ 
    Thread.Sleep(4000);
    Console.WriteLine("t1 is ending.");
});
t1.Start();

Thread t2 = new Thread(() => 
{ 
    Thread.Sleep(1000);
    Console.WriteLine("t2 is ending.");
});
t2.Start();

t1.Join();
Console.WriteLine("t1.Join() returned.");

t2.Join();
Console.WriteLine("t2.Join() returned.");

我希望这会有所帮助。


编辑。解决您的 cmets 问题;对 Join() 的调用是取消分配线程的原因。你不必做任何其他事情。只需确保线程在退出之前清理它们可能正在使用的所有资源。

也就是说,我强烈建议您考虑使用线程池或任务并行库 (TPL),而不是显式管理线程。它们更容易使用,处理这类事情也更顺畅。

【讨论】:

  • 如果您查看代码,似乎他将要求线程加入自身...Running_code() 是线程,它可以访问Thread代表自己的对象t。各种乱七八糟的!
  • 同意。我也同意上面的评论,建议上面的代码运行正常。这并没有消除Abort() 令人讨厌的事实。
  • 如果在函数内部调用线程对象不是最佳实践,我如何确保函数完成后线程被销毁?
  • @Jordy 看我上面的编辑...
  • 啊,我错过了,谢谢!
【解决方案2】:

如果可以的话,您是否可以使用 .net 4 +,您可以使用 TPL,这将大大简化这一点

public class MyClass
    {
        public void Do_your_thing()
        {
            // for async execution
            Task.Factory.StartNew(Running_code);

            // for synchronous execution
            // CAUTION !! If invoked from UI thread this will freeze the GUI until Running_code is returned.
            //Task.Factory.StartNew(Running_code).Wait(); 
        }

        private void Running_code()
        {
           Thread.Sleep( 2000 );
           Debug.WriteLine( "Something was done" );
        }
    }

此外,如果 Running_Code 方法正在执行 IO 绑定的操作,则 TPL 可以利用 IO 完成端口,并且该操作可能完全是无线程的。

编辑:

看看这个 SO 线程。 WebBrowser Control in a new thread

显然 webbrowser 控件不能很好地与非 UI 线程配合使用。

【讨论】:

  • 这看起来是一个很好的解决方案,我发布了更多代码,表明我正在使用 WebBrowser 对象。它必须在一个线程中运行,所以我不确定我是否可以使用这个解决方案。
猜你喜欢
  • 1970-01-01
  • 2012-10-06
  • 2022-01-05
  • 2014-08-15
  • 2017-11-05
  • 2017-09-24
  • 2016-01-27
  • 1970-01-01
  • 2018-10-01
相关资源
最近更新 更多