none
How to avoid the cross-thread exception when implementing the behavior of a raised event from another thread? RRS feed

  • Question

  • What is the best solution for the following problem?

    Exception message: Cross-thread operation not valid: Control 'resultLabel' accessed from a thread other than the thread it was created on.

    public class Sleeper
    {
        public event EventHandler Awaken;
    
        public void Sleep(int timeout)
        {
            Thread.Sleep(timeout);
            Awaken?.Invoke(this, null);
        }
    
        public Task SleepAsync(int timeout)
        {
            return Task.Run(() => Sleep(timeout));
        }
    }
    public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();
        }
    
        private async void sleepButton_Click(object sender, EventArgs e)
        {
            Sleeper sleeper = new Sleeper();
            sleeper.Awaken += sleeper_Awaken;
    
            await sleeper.SleepAsync(3000);
    
            Console.WriteLine("I had to wait until you woke up to write this sentence.");
        }
    
        private void sleeper_Awaken(object sender, EventArgs e)
        {
            resultLabel.Text = "I just woke up!";
        }
    }

    Thanks for any solution provided!

    Saturday, August 19, 2017 7:15 AM

Answers

  • The proper approach is called Invoking. The Cross Thread exception has been added intentionally to all GUI classes to avoid issues of people not properly Invoking. So that exception is your friend and you have to change your design to avoid it entirely.

    If this is about learning proper Multithreading, I could adivse to using the BackgroundWorker. It does the nitty-gritty work of Invoking the proper Events and generally help you develop decent designs.


    Remember to mark helpfull answers as helpfull and close threads by marking answers.

    • Marked as answer by Yulio Granja Monday, August 21, 2017 7:35 AM
    Sunday, August 20, 2017 12:43 AM

All replies

  • Your code is complex, Could we just make it sample and easy to let you understand how logic it runs.

    First , we don't need Sleepr Class you make (You could do it after when you understand.)

    private async void sleepButton_Click(object sender, EventArgs e)
    {
    	Console.WriteLine("I had to wait until you woke up to write this sentence.");
    	sleeper_Awaken();
    	System.Threading.Thread.Sleep(3000);
    }
    
    private void sleeper_Awaken()
    {
    	resultLabel.Text = "I just woke up!";
    }

    Then it will run Console first , then sleeper_Awaken after the 3000s.




    Saturday, August 19, 2017 10:10 AM
  • The proper approach is called Invoking. The Cross Thread exception has been added intentionally to all GUI classes to avoid issues of people not properly Invoking. So that exception is your friend and you have to change your design to avoid it entirely.

    If this is about learning proper Multithreading, I could adivse to using the BackgroundWorker. It does the nitty-gritty work of Invoking the proper Events and generally help you develop decent designs.


    Remember to mark helpfull answers as helpfull and close threads by marking answers.

    • Marked as answer by Yulio Granja Monday, August 21, 2017 7:35 AM
    Sunday, August 20, 2017 12:43 AM