|
ms
newsgroups
|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
Events - thread safe?to prevent the UI from freezing up. My question is: Is the following code considered "thread safe"? If not, what am I doing wrong? Thanks Dan -------------------------------------------------------- I have a form with this code: -------------------------------------------------------- private void button1_Click(object sender, System.EventArgs e) { WorkerClass wc = new WorkerClass(); wc.ProcessComplete+= new WorkerClass.ProcessCompleteHandler(WorkerDone); wc.Process(); Console.WriteLine("Waiting for worker thread"); } public void WorkerDone(long current) { Console.WriteLine("main: Worker thread complete" + current.ToString()); this.textBox1.Text = current.ToString(); } -------------------------------------------------------- A also have a seperate class with this code: -------------------------------------------------------- using System; using System.Threading; namespace TestWorkerThread { /// <summary> /// Summary description for WorkerClass. /// </summary> public class WorkerClass { long Result; public delegate void ProcessCompleteHandler(long current); public event ProcessCompleteHandler ProcessComplete; public WorkerClass() { // // TODO: Add constructor logic here // } public void Process() { // Create a new thread and call a function on that new thread. // This method should return without waiting ThreadStart myThreadDelegate = new ThreadStart(ProcessData); Thread myThread = new Thread(myThreadDelegate); myThread.Start(); } private void ProcessData() { Result = 0; for (int x = 0; x<10; x++) { Result = Result + x; Thread.Sleep(1000); } // Fire the event signal. Any subscribers will then know the processing is done. OnProcessComplete(Result); } protected void OnProcessComplete(long current) { ProcessComplete(current); } } } ---------------------------------------------------------------- Dan,
Events are fired on the thread that they are called from. Because of this, your ProcessCompleteHandler which sets the text in the textbox is not correct. What you need to do is have your call to set the Text property of the TextBox on the UI thread. You can do this like this: // Define the delegate. delegate void WorkerDoneCallback(long current); Then, when you set up the callback for the ProcessCompleteHandler, I would do the following: wc.ProcessComplete += delegate(long current) { // Call the WorkerDone method on the UI thread. this.Invoke(new WorkerClass.ProcessCompleteHandler(WorkerDone), new object[1]{current}); } Basically, you have set up an anonymous delegate which will call your WorkerDone method through the Invoke method on the Control. Hope this helps. -- Show quoteHide quote- Nicholas Paldino [.NET/C# MVP] - mvp@spam.guard.caspershouse.com "Dan Tallent" <spam@microsoft.com> wrote in message news:e%237Up$DSGHA.256@TK2MSFTNGP14.phx.gbl... >I am trying to learn how to create an application that uses worker threads >to prevent the UI from freezing up. > My question is: Is the following code considered "thread safe"? > If not, what am I doing wrong? > > Thanks > Dan > > -------------------------------------------------------- > I have a form with this code: > -------------------------------------------------------- > private void button1_Click(object sender, System.EventArgs e) > > { > > WorkerClass wc = new WorkerClass(); > > wc.ProcessComplete+= new WorkerClass.ProcessCompleteHandler(WorkerDone); > > wc.Process(); > > Console.WriteLine("Waiting for worker thread"); > > } > > public void WorkerDone(long current) > > { > > Console.WriteLine("main: Worker thread complete" + current.ToString()); > > this.textBox1.Text = current.ToString(); > > } > > > > -------------------------------------------------------- > > A also have a seperate class with this code: > > -------------------------------------------------------- > > using System; > > using System.Threading; > > namespace TestWorkerThread > > { > > /// <summary> > > /// Summary description for WorkerClass. > > /// </summary> > > public class WorkerClass > > { > > long Result; > > public delegate void ProcessCompleteHandler(long current); > > public event ProcessCompleteHandler ProcessComplete; > > > > public WorkerClass() > > { > > // > > // TODO: Add constructor logic here > > // > > > } > > public void Process() > > { > > > // Create a new thread and call a function on that new thread. > > // This method should return without waiting > > > ThreadStart myThreadDelegate = new ThreadStart(ProcessData); > > Thread myThread = new Thread(myThreadDelegate); > > myThread.Start(); > > > > } > > private void ProcessData() > > { > > Result = 0; > > > for (int x = 0; x<10; x++) > > { > > Result = Result + x; > > Thread.Sleep(1000); > > } > > > // Fire the event signal. Any subscribers will then know the processing is > done. > > OnProcessComplete(Result); > > } > > protected void OnProcessComplete(long current) > > { > > ProcessComplete(current); > > } > > } > > } > > > > ---------------------------------------------------------------- > > > > > > > > > > Dan,
No, it is not thread-safe. Windows controls can only be accessed from the thread they were created on. Typically, that's the main UI thread. What you need to do is marshal the execution of a delegate onto that thread using Control.Invoke. public void WorkerDone(long current) { if (this.InvokeRequired) { Delegate method = new ProcessCompleteHandler(this.WorkerDone); object[] args = new object[] { current }; this.Invoke(method, args); } else { Console.WriteLine("main: Worker thread complete" + current.ToString()); this.textBox1.Text = current.ToString(); } } Read the following article for more information. <http://www.yoda.arachsys.com/csharp/threads/winforms.shtml> Brian Dan Tallent wrote: Show quoteHide quote > I am trying to learn how to create an application that uses worker threads > to prevent the UI from freezing up. > My question is: Is the following code considered "thread safe"? > If not, what am I doing wrong? > > Thanks > Dan > > -------------------------------------------------------- > I have a form with this code: > -------------------------------------------------------- > private void button1_Click(object sender, System.EventArgs e) > > { > > WorkerClass wc = new WorkerClass(); > > wc.ProcessComplete+= new WorkerClass.ProcessCompleteHandler(WorkerDone); > > wc.Process(); > > Console.WriteLine("Waiting for worker thread"); > > } > > public void WorkerDone(long current) > > { > > Console.WriteLine("main: Worker thread complete" + current.ToString()); > > this.textBox1.Text = current.ToString(); > > } > > > > -------------------------------------------------------- > > A also have a seperate class with this code: > > -------------------------------------------------------- > > using System; > > using System.Threading; > > namespace TestWorkerThread > > { > > /// <summary> > > /// Summary description for WorkerClass. > > /// </summary> > > public class WorkerClass > > { > > long Result; > > public delegate void ProcessCompleteHandler(long current); > > public event ProcessCompleteHandler ProcessComplete; > > > > public WorkerClass() > > { > > // > > // TODO: Add constructor logic here > > // > > > } > > public void Process() > > { > > > // Create a new thread and call a function on that new thread. > > // This method should return without waiting > > > ThreadStart myThreadDelegate = new ThreadStart(ProcessData); > > Thread myThread = new Thread(myThreadDelegate); > > myThread.Start(); > > > > } > > private void ProcessData() > > { > > Result = 0; > > > for (int x = 0; x<10; x++) > > { > > Result = Result + x; > > Thread.Sleep(1000); > > } > > > // Fire the event signal. Any subscribers will then know the processing is > done. > > OnProcessComplete(Result); > > } > > protected void OnProcessComplete(long current) > > { > > ProcessComplete(current); > > } > > } > > } > > > > ---------------------------------------------------------------- I could see how this could work, but what if I want the function in the
worker thread to be independent of a specific form? I thought using subscribing to events in this manner would allow this. I will take a look at the link you supplied. Thanks Dan Show quoteHide quote "Brian Gideon" <briangid***@yahoo.com> wrote in message news:1142436174.738791.307280@e56g2000cwe.googlegroups.com... > Dan, > > No, it is not thread-safe. Windows controls can only be accessed from > the thread they were created on. Typically, that's the main UI thread. > What you need to do is marshal the execution of a delegate onto that > thread using Control.Invoke. > > public void WorkerDone(long current) > { > if (this.InvokeRequired) > { > Delegate method = new ProcessCompleteHandler(this.WorkerDone); > object[] args = new object[] { current }; > this.Invoke(method, args); > } > else > { > Console.WriteLine("main: Worker thread complete" + > current.ToString()); > this.textBox1.Text = current.ToString(); > } > } > > > Read the following article for more information. > > <http://www.yoda.arachsys.com/csharp/threads/winforms.shtml> > > Brian > > Dan Tallent wrote: >> I am trying to learn how to create an application that uses worker >> threads >> to prevent the UI from freezing up. >> My question is: Is the following code considered "thread safe"? >> If not, what am I doing wrong? >> >> Thanks >> Dan >> >> -------------------------------------------------------- >> I have a form with this code: >> -------------------------------------------------------- >> private void button1_Click(object sender, System.EventArgs e) >> >> { >> >> WorkerClass wc = new WorkerClass(); >> >> wc.ProcessComplete+= new WorkerClass.ProcessCompleteHandler(WorkerDone); >> >> wc.Process(); >> >> Console.WriteLine("Waiting for worker thread"); >> >> } >> >> public void WorkerDone(long current) >> >> { >> >> Console.WriteLine("main: Worker thread complete" + current.ToString()); >> >> this.textBox1.Text = current.ToString(); >> >> } >> >> >> >> -------------------------------------------------------- >> >> A also have a seperate class with this code: >> >> -------------------------------------------------------- >> >> using System; >> >> using System.Threading; >> >> namespace TestWorkerThread >> >> { >> >> /// <summary> >> >> /// Summary description for WorkerClass. >> >> /// </summary> >> >> public class WorkerClass >> >> { >> >> long Result; >> >> public delegate void ProcessCompleteHandler(long current); >> >> public event ProcessCompleteHandler ProcessComplete; >> >> >> >> public WorkerClass() >> >> { >> >> // >> >> // TODO: Add constructor logic here >> >> // >> >> >> } >> >> public void Process() >> >> { >> >> >> // Create a new thread and call a function on that new thread. >> >> // This method should return without waiting >> >> >> ThreadStart myThreadDelegate = new ThreadStart(ProcessData); >> >> Thread myThread = new Thread(myThreadDelegate); >> >> myThread.Start(); >> >> >> >> } >> >> private void ProcessData() >> >> { >> >> Result = 0; >> >> >> for (int x = 0; x<10; x++) >> >> { >> >> Result = Result + x; >> >> Thread.Sleep(1000); >> >> } >> >> >> // Fire the event signal. Any subscribers will then know the processing >> is >> done. >> >> OnProcessComplete(Result); >> >> } >> >> protected void OnProcessComplete(long current) >> >> { >> >> ProcessComplete(current); >> >> } >> >> } >> >> } >> >> >> >> ---------------------------------------------------------------- > Ok, I think I get it.
Because the calling function is on a different thread, I need to use the Invoke method for the WorkerDone method. And writting the WorkerDone method in this way prevents the WorkerClass from "needing to know" anything about the form. The InvokeRequired property is an "allowed" exception to the rule, which allows me to call it either directly or marshalling it if required. If I am wrong with this description, please let me know. I really appreciate your help. Thanks Dan Show quoteHide quote "Dan Tallent" <spam@microsoft.com> wrote in message news:OQQtsiESGHA.2224@TK2MSFTNGP10.phx.gbl... >I could see how this could work, but what if I want the function in the >worker thread to be independent of a specific form? I thought using >subscribing to events in this manner would allow this. I will take a >look at the link you supplied. Thanks > > Dan > > "Brian Gideon" <briangid***@yahoo.com> wrote in message > news:1142436174.738791.307280@e56g2000cwe.googlegroups.com... >> Dan, >> >> No, it is not thread-safe. Windows controls can only be accessed from >> the thread they were created on. Typically, that's the main UI thread. >> What you need to do is marshal the execution of a delegate onto that >> thread using Control.Invoke. >> >> public void WorkerDone(long current) >> { >> if (this.InvokeRequired) >> { >> Delegate method = new ProcessCompleteHandler(this.WorkerDone); >> object[] args = new object[] { current }; >> this.Invoke(method, args); >> } >> else >> { >> Console.WriteLine("main: Worker thread complete" + >> current.ToString()); >> this.textBox1.Text = current.ToString(); >> } >> } >> >> >> Read the following article for more information. >> >> <http://www.yoda.arachsys.com/csharp/threads/winforms.shtml> >> >> Brian >> >> Dan Tallent wrote: >>> I am trying to learn how to create an application that uses worker >>> threads >>> to prevent the UI from freezing up. >>> My question is: Is the following code considered "thread safe"? >>> If not, what am I doing wrong? >>> >>> Thanks >>> Dan >>> >>> -------------------------------------------------------- >>> I have a form with this code: >>> -------------------------------------------------------- >>> private void button1_Click(object sender, System.EventArgs e) >>> >>> { >>> >>> WorkerClass wc = new WorkerClass(); >>> >>> wc.ProcessComplete+= new WorkerClass.ProcessCompleteHandler(WorkerDone); >>> >>> wc.Process(); >>> >>> Console.WriteLine("Waiting for worker thread"); >>> >>> } >>> >>> public void WorkerDone(long current) >>> >>> { >>> >>> Console.WriteLine("main: Worker thread complete" + current.ToString()); >>> >>> this.textBox1.Text = current.ToString(); >>> >>> } >>> >>> >>> >>> -------------------------------------------------------- >>> >>> A also have a seperate class with this code: >>> >>> -------------------------------------------------------- >>> >>> using System; >>> >>> using System.Threading; >>> >>> namespace TestWorkerThread >>> >>> { >>> >>> /// <summary> >>> >>> /// Summary description for WorkerClass. >>> >>> /// </summary> >>> >>> public class WorkerClass >>> >>> { >>> >>> long Result; >>> >>> public delegate void ProcessCompleteHandler(long current); >>> >>> public event ProcessCompleteHandler ProcessComplete; >>> >>> >>> >>> public WorkerClass() >>> >>> { >>> >>> // >>> >>> // TODO: Add constructor logic here >>> >>> // >>> >>> >>> } >>> >>> public void Process() >>> >>> { >>> >>> >>> // Create a new thread and call a function on that new thread. >>> >>> // This method should return without waiting >>> >>> >>> ThreadStart myThreadDelegate = new ThreadStart(ProcessData); >>> >>> Thread myThread = new Thread(myThreadDelegate); >>> >>> myThread.Start(); >>> >>> >>> >>> } >>> >>> private void ProcessData() >>> >>> { >>> >>> Result = 0; >>> >>> >>> for (int x = 0; x<10; x++) >>> >>> { >>> >>> Result = Result + x; >>> >>> Thread.Sleep(1000); >>> >>> } >>> >>> >>> // Fire the event signal. Any subscribers will then know the processing >>> is >>> done. >>> >>> OnProcessComplete(Result); >>> >>> } >>> >>> protected void OnProcessComplete(long current) >>> >>> { >>> >>> ProcessComplete(current); >>> >>> } >>> >>> } >>> >>> } >>> >>> >>> >>> ---------------------------------------------------------------- >> > > Dan Tallent wrote:
> Ok, I think I get it. Yeah, that's right. The WorkerClass doesn't know anything about who is> > Because the calling function is on a different thread, I need to use the > Invoke method for the WorkerDone method. > And writting the WorkerDone method in this way prevents the WorkerClass from > "needing to know" anything about the form. subscribed to its events. But, the form knows how the WorkerClass behaves. Specifically, it knows that the WorkerClass' events are not guarenteed to be executed on the UI thread so the form needs to ensure that. There is an alternate design pattern that I've used before. Take a look at the System.Timers.Timer class. You'll notice that it has a SynchronizingObject property that accepts an ISynchronizeInvoke object. Forms and Controls implement ISynchronizeInvoke. Actually, that's the interface that contains the InvokeRequired, Invoke, etc. methods. When that property is set to a form or control it will automatically marshal events and callbacks onto the thread hosting that object. You could do something similar with your WorkerClass. > The InvokeRequired property is an "allowed" exception to the rule, which Yes, InvokeRequired, Invoke, and BeginInvoke are all thread-safe. They> allows me to call it either directly or marshalling it if required. > are among the few exceptions. Show quoteHide quote > If I am wrong with this description, please let me know. > > I really appreciate your help. > > Thanks > Dan > >
Other interesting topics
Reading keys from HKEY_CURRENT_USER from an impersonated Web Service
static class confirmation Assemlby.LoadForm() exception Out of the Box Microsoft Feature - Tricking out your application questions C# application listen to update A problem with UserControls and MessageBoxes wrapper in .NET 1.1 to call a .NET 2 XML & XSD Schema validation - "all" unmoveable Form Debugging referenced assembly? |
|||||||||||||||||||||||