I've got thread, which processes some analytic work.
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);
}
}
I want safely update my gridView at loop, so I use classic way:
private delegate void UpdateGridDelegate(DataGridView grid, int rowIdx, int type);
public static void UpdateGridSafe(DataGridView grid, int rowIdx, int type开发者_StackOverflow社区)
{
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;
}
}
But when I enter UpdateGridSafe, the program hangs.
In the debugger, I see that grid.Invoke doesn't invoke UpdateGridSafe. Please help - what's wrong?
EDIT
Classic thread create code
Thread t = new Thread(new ParameterizedThreadStart(ThreadProc));
t.Start(dgvSource);
t.Join();
MessageBox.Show("Done", "Info");
You have a deadlock. Your t.Join is blocking the GUI thread until ThreadProc is done. ThreadProc is blocked waiting for t.Join to finish so it can do the Invokes.
Bad Code
Thread t = new Thread(new ParameterizedThreadStart(ThreadProc));
t.Start(dgvSource);
t.Join(); <--- DEADLOCK YOUR PROGRAM
MessageBox.Show("Done", "Info");
Good Code
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");
}
EDIT
Also use BeginInvoke instead of Invoke. That way your worker thread doesn't have to block every time you update the GUI.
Reference
Avoid Invoke(), prefer BeginInvoke()
It's because you're joining on your worker thread. Your UI thread starts the background thread, then calls Join on it. This stops the UI thread from performing any other actions.
During this, the background thread is doing its work and calling Invoke, which waits for the UI thread to respond. Because the UI thread is waiting for a Join, it won't ever process the request to invoke. Hence, deadlock.
What you should do, is eliminate the Join and the MessageBox. Put the MessageBox into its own function.
void NotifyDone() {
if( InvokeRequired ) BeginInvoke( (MethodInvoker) NotifyDone );
else {
// Perform any post-processing work
MessageBox.Show("Done", "Info");
}
}
When the background thread is done, simply call this method (and eliminate static from 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();
}
And like everyone else has already said, the use of Sleep, especially at such a low interval is either dangerous, misleading or worthless. I'm in the count it worthless camp.
The Invoke statement will wait until the main thread's message pump isn't busy, and can handle a new message. If your main thread is busy, the Invoke will hang.
In your case, it looks like your top code is running in a tight loop, so there's never a chance for the Invoke in the bottom code to actually run. If you change the Thread.Sleep in your upper code block to something with a time in it, hopefully that will give your main thread a chance to handle the .Invoke call.
Depending on what your main application thread is doing, you may need to actually finish your first loop before any of the .Invoke calls will run - if that's the case, I can post some modified code that might work better.
Never, ever, every use Thread.Sleep(0). It doesn't do what you think it does and will cause you nothing but pain. For example, in a tight loop the OS may decide that the thread that just slept is the next one to run. As a result you won't actually yield the thread.
Try your code again using Thread.Sleep(1) every N iterations where N is about 0.25 to 1.0 seconds worth of work.
If that doesn't work let me know and we can look at how ThreadProc is created.
References
Never Sleep(0) in an Infinite Loop
EDIT
Argument for never using Thread.Sleep
Thread.Sleep is a sign of a poorly designed program.
You also could be having issues accessing the grid at the same time from different threads. DataTables are not thread safe, so I'd guess that DataGridView isn't either. Here's some sample code from this article on DataRow and Concurrency where you would use Monitor.Enter and Montori.Exit to get some concurrency in place.
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);
}
}
}
精彩评论