c#线程问题使用从后台线程调用
我有线程,它处理一些分析工作。c#线程问题使用从后台线程调用
private static void ThreadProc(object obj)
{
var grid = (DataGridView)obj;
foreach (DataGridViewRow row in grid.Rows)
{
if (Parser.GetPreparationByClientNameForSynonims(row.Cells["Prep"].Value.ToString()) != null)
UpdateGridSafe(grid,row.Index,1);
Thread.Sleep(10);
}
}
我想安全地循环更新我的GridView,所以我用经典的方式:
private delegate void UpdateGridDelegate(DataGridView grid, int rowIdx, int type);
public static void UpdateGridSafe(DataGridView grid, int rowIdx, int type)
{
if (grid.InvokeRequired)
{
grid.Invoke(new UpdateGridDelegate(UpdateGridSafe), new object[] { grid, rowIdx, type });
}
else
{
if (type == 1)
grid.Rows[rowIdx].Cells["Prep"].Style.ForeColor = Color.Red;
if (type==2)
grid.Rows[rowIdx].Cells["Prep"].Style.ForeColor = Color.ForestGreen;
}
}
但是,当我进入UpdateGridSafe,程序挂起。
在调试器中,我看到grid.Invoke不调用UpdateGridSafe。请帮忙 - 怎么了?
编辑
经典线程创建代码
Thread t = new Thread(new ParameterizedThreadStart(ThreadProc));
t.Start(dgvSource);
t.Join();
MessageBox.Show("Done", "Info");
你有一个僵局。您的t.Join会阻止GUI线程,直到完成ThreadProc。 ThreadProc被阻塞等待t.Join完成,因此它可以完成调用。
糟糕的代码
Thread t = new Thread(new ParameterizedThreadStart(ThreadProc));
t.Start(dgvSource);
t.Join(); <--- DEADLOCK YOUR PROGRAM
MessageBox.Show("Done", "Info");
优良程序代码
backgroundWorker1.RunWorkerAsync
private void backgroundWorker1_DoWork(object sender,
DoWorkEventArgs e)
{
var grid = (DataGridView)obj;
foreach (DataGridViewRow row in grid.Rows)
{
if (Parser.GetPreparationByClientNameForSynonims(row.Cells["Prep"].Value.ToString()) != null)
UpdateGridSafe(grid,row.Index,1);
// don't need this Thread.Sleep(10);
}
}
private void backgroundWorker1_RunWorkerCompleted(
object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("Done", "Info");
}
编辑
而且使用的BeginInvoke,而不是调用。这样,每当你更新GUI时,你的工作线程就不必阻塞。
参考
的调用语句将等到主线程的消息泵不旺,并能处理新的消息。如果您的主线程繁忙,则调用将挂起。
就你而言,它看起来像你的*代码运行在一个紧密的循环中,所以底层代码中的Invoke从来没有机会真正运行。如果将上层代码块中的Thread.Sleep更改为有时间的内容,希望这会让主线程有机会处理.Invoke调用。
根据你的主应用程序线程在做什么,你可能需要在任何.Invoke调用运行之前真正完成你的第一个循环 - 如果是这样的话,我可以发布一些可能更好的修改代码。
我改为Thread.Sleep(10);但也得到了相同的情况:(主应用程序线程是ui线程,没有工作,请发布代码,可能会更好。 – 2010-06-04 04:00:01
@Andrew - 如果第一个代码块中的主线程是你的UI线程,而你在一个紧密的循环中运行它,然后它将不会响应任何.Invoke请求,直到它完成了正在运行的forEach中的每一行为止。如果您在另一个线程上调用ThreadProc(),那么您的消息泵应该可用 - .Invoke挂起的事实说明您阻止了您的主UI线程。找出您的主线程停止并解决的位置,并且您的.Invoke调用将再次开始工作。 – SqlRyan 2010-06-04 04:11:20
我创建ThreadProc来自主thrad,用于在后台处理一些工作 – 2010-06-04 04:19:16
从来没有,每次使用Thread.Sleep(0)。它不会做你认为它会做的事情,只会让你痛苦。例如,在紧密的循环中,操作系统可能会决定刚睡的线程是下一个要运行的线程。结果你实际上不会产生线程。
再次尝试使用Thread.Sleep(1)每次N次迭代的代码,其中N大约是0.25到1.0秒的工作量。
如果这不起作用,让我知道,我们可以看看如何创建ThreadProc。
参考
Never Sleep(0) in an Infinite Loop
编辑
性论证从未使用了Thread.Sleep
感谢您的回答。我尝试设置睡眠时间,但它没有帮助。另外我添加一些代码发布。 – 2010-06-04 04:11:36
这是因为你在你的工作线程的加盟。你的UI线程启动后台线程,然后调用Join。这会阻止UI线程执行任何其他操作。
在这期间,后台线程正在完成它的工作并调用Invoke,它将等待UI线程作出响应。因为UI线程正在等待Join,所以它不会处理要调用的请求。因此,僵局。
你应该做的是消除Join和MessageBox。将MessageBox放入它自己的函数中。
void NotifyDone() {
if(InvokeRequired) BeginInvoke((MethodInvoker) NotifyDone);
else {
// Perform any post-processing work
MessageBox.Show("Done", "Info");
}
}
当后台线程完成时,只需调用此方法(并从ThreadProc中消除静态)。
private void ThreadProc(object obj)
{
var grid = (DataGridView)obj;
foreach (DataGridViewRow row in grid.Rows)
{
if (Parser.GetPreparationByClientNameForSynonims(row.Cells["Prep"].Value.ToString()) != null)
UpdateGridSafe(grid,row.Index,1);
Thread.Sleep(10);
}
NotifyDone();
}
与其他人一样已经表示,使用睡眠,尤其是在如此低的区间或者是危险的,误导性的或毫无价值的。我认为这是毫无价值的阵营。
您也可以具有不同的线程同时访问电网的问题。 DataTables不是线程安全的,所以我猜DataGridView不是。以下是this article on DataRow and Concurrency中的一些示例代码,您可以使用Monitor.Enter和Montori.Exit来获得一些并发性。
public void DoWorkUpdatingRow(object state)
{
List<DataRow> rowsToWorkOn = (List<DataRow>)state;
foreach (DataRow dr in rowsToWorkOn)
{
Monitor.Enter(this);
try
{
dr["value"] = dr["id"] + " new value";
}
finally
{
Monitor.Exit(this);
}
}
}
你为什么使用thread.sleep?如果你想在更新网格后执行任何操作,你可以在这里使用回调函数吗? – Ram 2010-06-04 04:19:26