【问题标题】:See thread not correctly ended查看线程未正确结束
【发布时间】:2016-07-22 07:58:10
【问题描述】:

我正在使用 C# 和 WPF 在 Visual Studio 2015 上开发一个项目。有时我使用关闭命令退出正在运行的项目,有时使用停止调试按钮。问题是经过几次测试后,我的电脑开始变热,风扇发出噪音。我必须退出 Visual Studio 才能让机器平静下来。

所以我有问题:

  • 如何查看测试后没有结束的线程?
  • 当我知道他们时,如何正确地结束他们? (其实我Dispose一些线程当WindowClosing
  • 当我使用停止调试按钮时,如何确保线程正确结束?

谢谢

编辑:

有任务管理器的截图。当我启动应用程序时,CPU 从 5% 上升到 15%(或事件 25%)。 RAM 从 4GO 上升到 4.5。 当我停止应用程序时,CPU 会在几秒钟内达到 45% 并恢复到 5%,但 RAM 会达到 4.70GO 并且不会恢复。

EDIT2:

我在我的应用程序上创建了这种线程:

private bool isClosing = false;
public void Start()
{
    isClosing = false;
    ThreadPool.QueueUserWorkItem(new WaitCallback(doWorkThread));
}

public void Stop()
{
    isClosing = true;
}

private AutoResetEvent endPoolStateButton = new AutoResetEvent(false);
private void doWorkThread(object sender)
{
    Action action = new Action(() => doWork());
    while (!isClosing)
    {
        Thread.Sleep(100);
        this.Dispatcher.BeginInvoke(action, System.Windows.Threading.DispatcherPriority.Background);
    }
    endPoolStateButton.Set();
}

private void doWork()
{
    /* Job performed */
}

我想知道是否有一个非常好的使用线程的方法?如果应用程序在没有设置isClosing = true 的情况下关闭,则while 永远不会停止。而且线程从未真正中止过?你认为这种线程会引起我所有的麻烦吗?

【问题讨论】:

  • 查看任务管理器,看看发生了什么。如果应用程序在您“关闭”后仍在运行。如果它需要大量的RAM。如果 CPU 工作在 100%...
  • 您如何启动新线程以及它们何时以及如何终止? “处理一些线程”是什么意思?线程不是一次性的。而且您需要让所有前台线程在应用程序退出之前完成,而不仅仅是“一些”。
  • 我不是这个应用程序的唯一开发人员,在我开始工作之前已经开发了一些功能。所以我不知道所有线程是如何开始/结束的,这就是我问这个问题的原因。例如,我使用后台唤醒器并中止它(不是 Dispose,抱歉错误)created from there
  • @A.Pissicat,您可以使用我的工具github.com/REASY/ProcInfo4Net 获取有关线程状态的信息

标签: c# wpf multithreading


【解决方案1】:

这里是my solution 如何以优雅的方式停止线程。希望代码清晰。我使用CancellationToken 取消线程中的操作,使用ManualResetEvent 等待线程取消:

namespace ElegantThreadWork
{
    using System;
    using System.Threading;
    using System.Diagnostics;

    class ThreadObject
    {
        public CancellationToken CancellationToken { get; private set; }
        public ManualResetEvent WaitHandle { get; private set; }

        public ThreadObject(CancellationToken ct, ManualResetEvent wh)
        {
            CancellationToken = ct;
            WaitHandle = wh;
        }
    }
    public class Program
    {
        static void DoWork(CancellationToken ct)
        {
            Console.WriteLine("Thread[{0}] started", Thread.CurrentThread.ManagedThreadId);
            int i = 0;
            // Check for cancellation on each iteration
            while (!ct.IsCancellationRequested)
            {
                // Do something
                Console.WriteLine("Thread[{0}]: {1}", Thread.CurrentThread.ManagedThreadId, i);
                // Wait on CancellationToken. If cancel be called, WaitOne() will immediatly return control!
                // You can see it by elapsed time
                ct.WaitHandle.WaitOne(TimeSpan.FromSeconds(1));
                i++;
            }
            Console.WriteLine("Thread[{0}] has been cancelled", Thread.CurrentThread.ManagedThreadId);
        }
        static void ThreadProc(object state)
        {
            ThreadObject to = (ThreadObject)state;
            try
            {
                DoWork(to.CancellationToken);
            }
            finally
            {
                to.WaitHandle.Set();
            }
        }
        public static void Main(string[] args)
        {
            TimeSpan MAX_THREAD_EXITING_TIMEOUT = TimeSpan.FromSeconds(5);

            // Use for elegant thread exiting
            ManualResetEvent isThreadExitedEvent = new ManualResetEvent(false);
            CancellationTokenSource cts = new CancellationTokenSource();
            ThreadObject threadObj = new ThreadObject(cts.Token, isThreadExitedEvent);

            // Create thread
            Thread thread = new Thread(ThreadProc, 0);
            thread.Start(threadObj);

            Console.WriteLine("Just do something in main thread");

            Console.WriteLine("Bla.");
            Thread.Sleep(1000);

            Console.WriteLine("Bla..");
            Thread.Sleep(1000);

            Console.WriteLine("Bla...");
            Thread.Sleep(1000);

            Console.WriteLine("Thread cancelattion...");
            Stopwatch sw = Stopwatch.StartNew();
            // Cancel thread
            cts.Cancel();

            // Wait for thread exiting
            var isOk = isThreadExitedEvent.WaitOne(MAX_THREAD_EXITING_TIMEOUT);
            sw.Stop();
            Console.WriteLine("Waiting {0} for thread exiting. Wait result: {1}. Cancelled in {2}", MAX_THREAD_EXITING_TIMEOUT, isOk, sw.Elapsed);

            // If we couldn't stop thread in elegant way, just abort it
            if (!isOk)
                thread.Abort();
        }
    }
}

【讨论】:

  • 我会尝试这个解决方案,但在我需要列出在这个应用程序上创建的所有线程并且列表很长之前......大部分线程都是while (!close){...}类型并且它们永远不会中止.我很难把它们全部清理干净。
  • 如果您控制这些线程的创建,请使用Thread.IsBackground。后台线程与前台线程相同,只是后台线程不会阻止进程终止
【解决方案2】:

也许您可以尝试使用“Process Hacker”工具观察进程和线程的行为。使用此工具,您可以获得有关线程的更多详细信息,还可以检测守护线程。

另一种方法可能是:尝试获取主进程的所有子线程并执行类似的操作

Thread t1; // specific child thread 
t1.join(); 

【讨论】:

    猜你喜欢
    • 2018-09-16
    • 1970-01-01
    • 2015-01-02
    • 1970-01-01
    • 1970-01-01
    • 2013-05-10
    • 2016-04-30
    • 1970-01-01
    相关资源
    最近更新 更多