Łukasz Wiatrak
Łukasz Wiatrak

Reputation: 2767

Why a second UI thread freezes the first one?

I've got an application with two UI threads and one background worker. Everything works fine except that handling ProgressChanged in the second simulation UI thread blocks the first one. Why it happens? How can I workaround this (I want the second one be blocked instead of the main UI thread)?

Part of MainWindow class:

private SimulationWindow simulationWindow;

    public MainWindow()
    {
        InitializeComponent();
        Thread thread = new Thread(() =>
        {
            simulationWindow = new SimulationWindow();
            simulationWindow.Show();
            simulationWindow.Closed += (sender2, e2) =>
                      simulationWindow.Dispatcher.InvokeShutdown();
            Dispatcher.Run();
        });
        thread.SetApartmentState(ApartmentState.STA);
        thread.Start();
    }

    private void start_Click(object sender, RoutedEventArgs e)
    {
        simulationWindow.Start();
    }

Part of SimulationWindow class:

private BackgroundWorker bw;

public SimulationWindow()
{
    InitializeComponent();
    bw = new BackgroundWorker() { WorkerReportsProgress = true, WorkerSupportsCancellation = true };
    bw.DoWork += new DoWorkEventHandler(bw_DoWork);
    bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
    bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
}

void bw_DoWork(object sender, DoWorkEventArgs e)
{
    //some complex computation will go here
    //Thread.Sleep(10000); <- both windows responsive, OK
    bw.ReportProgress(0);
}


void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    //some complex rendering will go here
    Thread.Sleep(10000); // this blocks main UI thread, why?
}

public void Start()
{
    bw.RunWorkerAsync();
}

private void doSth_Click(object sender, RoutedEventArgs e)
{
    Thread.Sleep(10000); // freezes simulationWindow, which was understandable
}

Upvotes: 2

Views: 1143

Answers (1)

Nick Butler
Nick Butler

Reputation: 24383

You call simulationWindow.Start() on the main UI thread. This means the BW is started on that thread, which means it captures the SynchronizationContext of the main UI thread and so also raises ProgressChanged on that thread.

You need to marshal the call to bw.RunWorkerAsync() on to your second thread. You can do this in SimulationWindow.Start() by calling this.Dispatcher.Invoke and passing bw.RunWorkerAsync() as the delegate. It should then run on your second window thread and the events will be raised on that thread.

Upvotes: 6

Related Questions