Visual Studio 2015调试多线程应用程序无法工作

13
在我的项目中,有一部分代码需要在单独的线程上执行,而不会阻塞用户界面。当调试器在此代码中的断点处停下时,VS2015会冻结5-10秒钟。之后,如果我尝试继续调试(通过按Step Over、Step In或Continue),应用程序从暂停状态转换为工作状态,调试工具正在运行,但没有任何反应,CPU利用率为0%。如果我点击Break All,"光标"(不知道正确的术语)将显示在Program.cs中的处,在其中是。 由于我对C#相当陌生,所以认为我的多线程方法存在问题,但显然无论我使用什么方法都会出现这种情况——使用带有任务的async/await,使用BackgroundWorker组件或简单的new Thread(myFunc).Start()。 只想清楚地表达几点: - 代码本身完全正常工作。 - 调试器本身也可以工作,我的主线程上的断点没有死锁或卡死。如果我从主线程启动代码,一切都很好。 - 我还在一个简单的函数上进行了检查——同样的问题。 - 在不同版本的.NET上进行了测试:4.6、4.5、4.0。到处都一样。 - 问题在VS2010中不会出现(我以前使用过),在VS2013中也不会出现(我只是为了确保它是一个VS2015的问题)。然而,我的朋友使用同样的VS2015时也没有这个问题。 - 根据要求,测试表单代码如下,我一直遇到问题。只有三个按钮、标签和BackgroundWorker。总体方案类似于主项目的代码。
    public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    const int period = 10000;
    void FuncAsync(IProgress<int> progress)
    {
        for ( int i = 0; i < Int32.MaxValue; ++i )
        {
            double part = (double)i / Int32.MaxValue;
            int percent = (int)(part * 100.0);

            if ( (i % period) == 0 )
                progress.Report( percent );
        }
    }
    void FuncBW(BackgroundWorker worker)
    {
        for ( int i = 0; i < Int32.MaxValue; ++i )
        {
            double part = (double)i / Int32.MaxValue;
            int percent = (int)(part * 100.0);

            if ( (i % period) == 0 )
                worker.ReportProgress( percent );
        }
    }
    void FuncThread()
    {
        for ( int i = 0; i < Int32.MaxValue; ++i )
        {
            double part = (double)i / Int32.MaxValue;
            int percent = (int)(part * 100.0);

            if ( (i % period) == 0 )
                label1.Text = percent.ToString();
            //yes, this one will cause exception of accessing UI from different thread
            //if i press "Break" in the exception window, i will also get a 10sec freeze
        }
    }



    private async void button1_Click(object sender, EventArgs e)
    {
        var progress = new Progress<int>(i => label1.Text = i.ToString() );
        await Task.Factory.StartNew( () => FuncAsync( progress ),
                                    TaskCreationOptions.LongRunning );
    }

    private void button2_Click(object sender, EventArgs e)
    {
        backgroundWorker1.RunWorkerAsync();
    }

    private void button3_Click(object sender, EventArgs e)
    {
        Thread t = new Thread(FuncThread);
        t.Start();
    }

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        FuncBW( (BackgroundWorker)sender );
    }

    private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        label1.Text = e.ProgressPercentage.ToString();
    }
}

1
这段代码存在第三个最常见的线程问题,即火管虫。你以极高的速度轰击 UI 线程,它烧掉了 100% 的核心但仍然无法跟上节奏。它停止响应用户输入和绘制。是的,你会注意到。编写理智的代码,每秒约20次更新后,它只会开始模糊人眼。没有必要更快。 - Hans Passant
2
@HansPassant,我提供这段代码只是为了举一个简单但仍能重现问题的例子。在我的主项目中,我使用一个包含许多文档的数据库,每个文档需要大约1-2分钟进行解析,然后在屏幕上将计数器加1。我只是认为没有必要复制粘贴一堵复杂的文字墙。 - Artalus
创建一个模型是个好主意,但要确保你没有通过简化引入另一个问题(Hans提到的那个)。插入一些延迟并确保你仍然能够重现这个问题。 - H H
7个回答

23

我曾经遇到VS2015调试多线程WinForms应用程序时卡死的类似问题。

但在VS2013中,我没有遇到过这样的问题。

当我禁用VS托管进程(Project -> Properties -> Debug -> Enable the Visual Studio hosting process)后,该问题似乎就消失了。

希望对其他人有所帮助。


2
非常感谢!那真的帮了我很多! - Artalus

1
在选项-调试-常规中勾选“使用托管兼容模式”后,线程调试似乎可以正常工作。

这就是我想要的。我一直在追寻这个问题的答案,你的回答是唯一有效的。谢谢! - djangojazz
注意,这也会防止编辑和继续。 - Jim Balkwill

0

我在2个不同的线程中设置了断点,当我逐步执行时,Visual Studio会在这两个线程之间跳转。最终,标题栏中出现(未响应)并冻结。

如果将断点限制在一个线程中,我就不会遇到这个问题。


0

我曾经遇到过类似的问题。这个问题的其他答案并没有解决我的问题,但是它们指引了我正确的方向。显然,我选择了发布配置,而不是调试


自从我重新安装了VS,我就再也没有遇到过这个bug了,但我记得以前不是这样的,因为它在Debug和Release模式下都会出现。不过很高兴听到你解决了这个问题! - Artalus

0
经过烦人的几个小时,@Haggisatonal 的答案对我非常有用。非常感谢!
引用:
问题似乎在禁用 VS 托管进程后消失了(项目 -> 属性 -> 调试 -> 启用 Visual Studio 托管进程)。
但是,“工具 -> 选项 -> 调试 -> 通用 -> 启用属性评估和其他隐式函数调用”,就像另一个票据中所说的那样,对我来说不是解决方案,也许它可以暂时帮助其他人。

-1

我遇到了Visual Studio 2008冻结的问题,即使禁用了托管进程。对我来说似乎有效的方法是禁用地址级调试。

(VS2008) 工具(菜单)-> 选项 -> 调试 -> 通用 -> (取消勾选)启用地址级调试。


-1
我建议您在代码中使用定时器和 WaitHandle 的组合,而不是 for 循环,因为后者会导致 CPU 使用率高。我对您的代码进行了简单的更改,以减轻 CPU 使用率。希望这可以帮到您。
    public partial class Form1 : Form
    {
        EventWaitHandle _waitHandle ;
        System.Timers.Timer _timer; 
        public Form1()
        {
            InitializeComponent();
            _waitHandle = new EventWaitHandle(false, EventResetMode.AutoReset);
            _timer = new System.Timers.Timer();
            _timer.Interval = 100;
            _timer.Elapsed += OnTimerElapsed;
            _timer.AutoReset = true;

        }

        private void OnTimerElapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            _waitHandle.Set();
        }

        void FuncAsync(IProgress<int> progress)
        {
            _timer.Start();
            int percent = 0;
            while (percent <= 100)
            {
                if (_waitHandle.WaitOne())
                {
                    progress.Report(percent);
                    percent++;
                }
            }
            _timer.Stop();

        }
        void FuncBW(BackgroundWorker worker)
        {
            _timer.Start();
            int percent = 0;
            while (percent <= 100)
            {
                if (_waitHandle.WaitOne())
                {
                    worker.ReportProgress(percent);
                    percent++;
                }
            }
            _timer.Stop();
        }



        void FuncThread()
        {
            _timer.Start();
            int percent = 0;
            while (percent <= 100)
            {
                if (_waitHandle.WaitOne())
                {
                    if (this.InvokeRequired)
                    {
                        this.Invoke((Action)delegate { label1.Text = percent.ToString(); });
                    }
                    else
                    {
                        label1.Text = percent.ToString();
                    }
                    percent++;
                }
            }
            _timer.Stop();
        }


        ... your other code
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接