Is C # event execution thread safe?

I read a lot of discussions on topics and topics, but they all focus on “what happens” if I can’t subscribe to the event and try to call him later. My question is different ... what happens if I have a process in thread A that fires the event "I finish" in millisecond 1, and also execute a process in thread B that fires the event "I finish" in millisecond 2.

Both processes eavesdrop on the same method for listening and handling the event. Thus, C # must execute a method that processes the event 2 times: 1 time for the event fired in thread A, and 1 time for the event fired from thread B.

What will happen? C # blocks a method when the "first event coming from thread A" starts execution of a method that processes the event, and unlocks a method when it finishes execution, thereby allowing other expectations of the "event" to execute the method content?

Or an event fired from thread A will start the method that processes the event, and after 1 millisecond, the event that was fired from thread B will also start by the same method, not noticing that this method is currently being executed by another "process" "

I ask this because I want to record a file in a method that catches an event, but if the method can be executed at the same time (depending on when this event is fired), I think I can not do it here, since the information about the file will be a mixture between two processes written to the same file at the same time (incorrect file information).

My code looks like this (a little long, sorry). note that this does not compile, just a sample to show what I'm doing:

public partial class MainForm : Form { FTPClientManager client = null; public MainForm() { InitializeComponent(); } private void btnConnect_Click(object sender, EventArgs e) { Connect(this.tbFTPServer.Text.Trim()); this.lstLog.Items.Add("Connected"); //This lstLog is a list box that will have a list of downloaded files from all threads. } void Connect(string urlStr) { try { client = new FTPClientManager(); //subscribe to the event client.FileDownloadCompleted += new EventHandler<FileDownloadCompletedEventArgs>(client_FileDownloadCompleted); } catch (Exception ex) { MessageBox.Show(ex.Message); } } void client_FileDownloadCompleted(object sender, FileDownloadCompletedEventArgs e) { this.Invoke(new EventHandler<FileDownloadCompletedEventArgs>( client_FileDownloadCompletedHandler), sender, e); } void client_FileDownloadCompletedHandler(object sender, FileDownloadCompletedEventArgs e) { string log = string.Format("{0} Instance {5} Download from {1} to {2} is completed. Length: {3} Time: {4}. ", DateTime.Now, e.ServerPath, e.LocalFile.FullName, e.LocalFile.Length, e.DownloadTime, e.ftpInstance); this.lstLog.Items.Add(log); } private void btnDownload_Click(object sender, EventArgs e) { client.DownloadFiles(); } } public class FTPClientManager { FTPDownloadClient[] arrayDownloadClient = new FTPDownloadClient[2]; public event EventHandler<FileDownloadCompletedEventArgs> FileDownloadCompleted; public void DownloadFiles() { for (int i = 0; i < 2; i++) { arrayDownloadClient[i] = new FTPDownloadClient(); //subscribe to the event. each instance of FTPDownloadClient will suscribe to the same event. arrayDownloadClient[i].FileDownloadCompleted += new EventHandler<FileDownloadCompletedEventArgs>(downloadClient_FileDownloadCompleted); } //download one set of files in thread A arrayDownloadClient[0].DownloadFiles(list_of_files_to_download); //download another set of files in thread B arrayDownloadClient[1].DownloadFiles(another_list_of_files_to_download); } //In theory, the method downloadClient_FileDownloadCompleted will be executed by any instance of FTPDownloadClient //running in either thread A or thread B, whichever finish first downloading a file. //My question comes in the execution of this method. //Lets say the process in thread A finish downloading and fires the event. //Lets say the process in thread B finish downloading 1 millisecond after thread A finish, so it also fires the event. //how C# manage the execution of the downloadClient_FileDownloadCompleted?? //does the event coming from thread A will lock the method downloadClient_FileDownloadCompleted, execute it, and when finish execution unlock the method //and allows the event coming from thread B start locking, processing, unlock ?? //Or the method will be executed "at the same time" (1 millisecond difference) by each event fired from thread A and thread B?? void downloadClient_FileDownloadCompleted(object sender, FileDownloadCompletedEventArgs e) { this.OnFileDownloadCompleted(e); } protected virtual void OnFileDownloadCompleted(FileDownloadCompletedEventArgs e) { if (FileDownloadCompleted != null) { //this will fire the event, so the main form will catch it //again, this fire can be triggered from process in thread A or from process in thread B FileDownloadCompleted(this, e); } } } public class FTPDownloadClient { public event EventHandler<FileDownloadCompletedEventArgs> FileDownloadCompleted; public void DownloadFiles(string [] files_to_download) { ParameterizedThreadStart threadStart = new ParameterizedThreadStart(StartDownloadFiles); Thread downloadThread = new Thread(threadStart); downloadThread.IsBackground = true; downloadThread.Start(new object[] { files_to_donwload }); } //This metod will download all the files in the list passed as parameter. //Every file downloaded will raise the event FileDownloadComplete, so a message can be added to the lstlog on the main form void StartDownloadFiles(object state) { var paras = state as object[]; string [] files = paras[0] as string []; foreach (var file in files) { DownloadOneFile(file); } } void DownloadFile(string onefile) { //Donwload file done here var fileDownloadCompletedEventArgs = new FileDownloadCompletedEventArgs { LocalFile = new FileInfo(destPath), ServerPath = onefile, DownloadTime = fileDownloadTime.ElapsedMilliseconds.ToString() }; this.OnFileDownloadCompleted(fileDownloadCompletedEventArgs); } protected virtual void OnFileDownloadCompleted(FileDownloadCompletedEventArgs e) { if (FileDownloadCompleted != null) { //the event is fired when the file being downloaded by this thread is finish. //so, thread A will fire this event from its current thread //and also thread B will fire the same event from its own thread. FileDownloadCompleted(this, e); } } } 
+7
source share
3 answers

C # will not block you. If events can be raised by several threads at the same time, you should write code to handle it (if necessary).

You can use the lock statement to prevent multiple threads from executing:

 private void MyEventHandler(object sender, EventArgs e) { lock (lockingObject) { // Handle event here. // Only one thread at a time can reach this code. } } 

Where lockingObject is the field inside your declared class:

 private readonly object lockingObject = new object(); 

You must also be careful with threads in the method that raises the event.

Suppose your class has a MyEvent event. You should not do this:

 private void RaiseMyEvent() { if (MyEvent != null) // {1} MyEvent(this, new EventArgs()); // {2} } 

If another thread can disconnect from MyEvent , then it is possible that it can disconnect between line {1} and line {2}. If this happens, line {2} will throw a null reference exception because MyEvent will suddenly become null!

The correct way to do this is:

 private void RaiseMyEvent() { var handler = MyEvent; if (handler != null) handler (this, new EventArgs()); } 

Now a null reference exception cannot occur.

However, note that when using multiple threads, it can be called by the event handler after the thread has separated it!

+12
source

It looks like you want to use a lock. This prevents multiple threads from executing the same code at a time. However, using a lock is not recommended and should be avoided most of the time, but it looks normal in your situation.

Block Statement

I like the example Microsoft gives you withdraw money

 int Withdraw(int amount) { // This condition never is true unless the lock statement // is commented out. if (balance < 0) { throw new Exception("Negative Balance"); } // Comment out the next line to see the effect of leaving out // the lock keyword. lock (thisLock) { if (balance >= amount) { Console.WriteLine("Balance before Withdrawal : " + balance); Console.WriteLine("Amount to Withdraw : -" + amount); balance = balance - amount; Console.WriteLine("Balance after Withdrawal : " + balance); return amount; } else { return 0; // transaction rejected } } } 
+1
source

It seems that you have already made your code stream safe because you are using Form.Invoke in your client_FileDownloadCompleted method.

It is true that the FTPClientManager.FileDownloadCompleted event can fire simultaneously for different threads, but Form.Invoke will serialize each call back to the main user interface thread. Therefore, in your code, you do not need a lock, because Form.Invoke will take it, and client_FileDownloadCompletedHandler will always be called in your user interface thread.

0
source

All Articles