从后台线程调用c#线程问题
我有一个线程,它处理一些分析工作从后台线程调用c#线程问题,c#,.net,multithreading,C#,.net,Multithreading,我有一个线程,它处理一些分析工作 private static void ThreadProc(object obj) { var grid = (DataGridView)obj; foreach (DataGridViewRow row in grid.Rows) { if (Parser.GetPreparationByClientNameForSynonims(row.Cells["Prep"].Val
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);
}
}
我想在loop安全地更新我的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");
Invoke语句将等待主线程的消息泵不忙,并且可以处理新消息。如果主线程正忙,调用将挂起 在您的例子中,您的顶层代码似乎在一个紧密的循环中运行,因此底层代码中的调用永远不可能真正运行。如果您将上层代码块中的Thread.Sleep更改为某个有时间的代码块,希望这会给您的主线程一个处理.Invoke调用的机会
根据您的主应用程序线程正在执行的操作,您可能需要在运行任何.Invoke调用之前实际完成第一个循环-如果是这种情况,我可以发布一些修改过的代码,这些代码可能会更好。永远不要每次使用线程。Sleep(0)。它不会做你认为它会做的事,只会给你带来痛苦。例如,在一个紧循环中,操作系统可能会决定刚刚休眠的线程是下一个要运行的线程。因此,您实际上不会产生线程 使用Thread.Sleep(1)每N次迭代重试一次代码,其中N大约是0.25到1.0秒的工作量 如果这不起作用,请告诉我,我们可以看看ThreadProc是如何创建的 参考资料 编辑 从不使用线程睡眠的理由
这是因为您正在加入工作线程。UI线程启动后台线程,然后对其调用Join。这将停止UI线程执行任何其他操作 在此期间,后台线程正在执行其工作并调用Invoke,它将等待UI线程响应。因为UI线程正在等待连接,所以它永远不会处理要调用的请求。因此,僵局 您应该做的是删除Join和MessageBox。将MessageBox放入它自己的函数中
void NotifyDone() {
if( InvokeRequired ) BeginInvoke( (MethodInvoker) NotifyDone );
else {
// Perform any post-processing work
MessageBox.Show("Done", "Info");
}
}
后台线程完成后,只需调用此方法(并从ThreadProc中消除static)
正如其他人已经说过的,睡眠的使用,特别是在如此低的间隔时间,要么是危险的,误导的,要么是毫无价值的。我在“数一数二”阵营。你陷入了僵局。您的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");
编辑
也可以使用BeginInvoke而不是Invoke。这样,您的工作线程就不必在每次更新GUI时都阻塞
参考文献
从不同线程同时访问网格时也可能遇到问题。DataTables不是线程安全的,所以我猜DataGridView也不是。下面是一些示例代码,您可以在其中使用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);
}
}
}
public void doworkUpdatengRow(对象状态)
{
List rowsToWorkOn=(列表)状态;
foreach(rowsToWorkOn中的数据行dr)
{
监视。输入(此);
尝试
{
dr[“value”]=dr[“id”]+新值;
}
最后
{
监控。退出(本);
}
}
}
我改为线程睡眠(10);但情况也一样:(.Main-app-thread是ui-thread。不工作。请发布可能工作得更好的代码。@Andrew-如果第一个代码块中的主线程是您的ui-thread,并且您在一个紧密循环中运行它,那么它将不会响应任何请求。调用请求,直到它处理完您正在运行的forEach中的每一行。如果您正在调用adProc()不过,在另一个线程上,您的消息泵应该是可用的,.Invoke挂起的事实表明您正在阻止主UI线程。找出主线程暂停的位置并解决该问题,然后,.Invoke调用将再次开始工作。我从主thrad创建ThreadProc,用于处理后面的一些工作ground@Andrew:您的编辑会将其清除。.Join会导致您的UI线程等待新线程以继续,这意味着当您的辅助线程尝试调用时,它会很忙。对它进行调用。为什么需要加入它?只需等待您创建的线程完成,然后引发一个事件,您可以在UI线程上处理该事件播放你的messagebox。谢谢你的回答。我尝试设置睡眠时间,但没有帮助。我还添加了一些代码来发布。为什么要使用thread.Sleep?如果你想在更新网格后执行任何操作,你可以在这里使用回调函数吗?
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");
}
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);
}
}
}