在我的项目中,有一部分代码需要在单独的线程上执行,而不会阻塞用户界面。当调试器在此代码中的断点处停下时,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();
}
}