8000 Dispatcher.Invoke() blocks Parallel.For execution on .NET 7 · Issue #8066 · dotnet/wpf · GitHub
[go: up one dir, main page]

Skip to content

Dispatcher.Invoke() blocks Parallel.For execution on .NET 7 #8066

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
simonedd opened this issue Aug 2, 2023 · 7 comments
Open

Dispatcher.Invoke() blocks Parallel.For execution on .NET 7 #8066

simonedd opened this issue Aug 2, 2023 · 7 comments
Labels
Investigate Requires further investigation by the WPF team. Priority:3 Work that is nice to have

Comments

@simonedd
Copy link
simonedd commented Aug 2, 2023

Description

When an Invoke() is called to update a UI object in the handler of a Threading.Timer, the execution of a simple Parallel.For gets stuck.

I report here this issue previously opened in dotnet/runtime because they said it's not a problem with the Paralle.For
I know I should avoid running a Parallel.For on the UI thread, but I think the execution should not hang.

Reproduction Steps

Here is a small WPF sample to reproduce the issue:

<Window x:Class="WpfAppNetCore.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfAppNetCore"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Label Name="Label1"  Content="Label" HorizontalAlignment="Left" Margin="356,0,0,0" VerticalAlignment="Center"/>
    </Grid>
</Window>
public partial class MainWindow : Window
{
    private System.Threading.Timer timer;
    private int count = 0;

    public MainWindow()
    {
        InitializeComponent();
    }

    protected override void OnContentRendered(EventArgs e)
    {
        timer = new Timer(TimerHandler, null, 0, 20);

        for (int i = 0; i < 100; i++)
        {
            Stopwatch sw = Stopwatch.StartNew();

            Parallel.For(0, 10, i =>
            {
                Thread.Sleep(1); // do some stuff
            });

            sw.Stop();
            Debug.WriteLine("elapsed time: " + sw.ElapsedMilliseconds);
        }

        base.OnContentRendered(e);
    }

    public void TimerHandler(Object stateInfo)
    {
        count++;

        Dispatcher.Invoke(() =>
        {
            Label1.Content = count;
        });
    }
}

Expected behavior

Parallel.For doesn't hang or slow down

Actual behavior

Parallel.For hangs

Regression?

The same application on .NET Framework doesn't produce so bad performance, the Parallel.For take about 150ms
On .NET 6 the Parallel.For take about 2 seconds but it doesn't stuck.

Known Workarounds

No response

Impact

No response

Configuration

.NET 7
Windows 11 version 21H2

Other information

No response

@miloush
Copy link
Contributor
miloush commented Aug 2, 2023

If you get into deadlock, things hang, that is expected.

Your UI update practice is bad and it does not make sense, because the value of count is not captured, so you are basically stalling the dispatcher with "show whatever is the latest count" operations, not "show the count value at the time this was invoked".

Use BeginInvoke to schedule your UI update operation and don't schedule another one until it is completed.

@singhashish-wpf singhashish-wpf added the 📭 waiting-author-feedback To request more information from author. label Aug 4, 2023
@simonedd
Copy link
Author
simonedd commented Aug 4, 2023

Yes, that makes sense, I should not block the dispatcher with many UI updates if the previous one is not completed.
Anyway, I'm not sure to understand where the deadlock is. Is it caused by the many threads blocked on the Invoke call?
I'd also be curious to know why it worked in .NET Framework

@ghost ghost removed the 📭 waiting-author-feedback To request more information from author. label Aug 4, 2023
@singhashish-wpf singhashish-wpf added Investigate Requires further investigation by the WPF team. Priority:3 Work that is nice to have labels Aug 7, 2023
@0xfa11b4c
Copy link

Just wanted to share a small demo confirming that replacing Dispatcher.Invoke() with Dispatcher.BeginInvoke() resolves the blocking and improves responsiveness.

I also added MaxDegreeOfParallelism to the Parallel.For, which seems to help as well.

Here's the test project:
https://github.com/0xfa11b4c/WpfDispatcherInvokeFix

Hope it's helpful for future reference.

@lindexi
Copy link
Member
lindexi commented May 13, 2025

@0xfa11b4c Thank you. And could you try the Parallel.ForAsync API?

@0xfa11b4c
Copy link

@lindexi Parallel.ForAsync is part of .net 8, right?
Should I try running it with .net 8?

@0xfa11b4c
Copy link

Tested with .NET 8 using Parallel.ForAsync. No blocking, UI stays smooth.

Code and logs are in the repo.

@lindexi
Copy link
Member
lindexi commented May 14, 2025

@0xfa11b4c Thank you very much. And I think it is reasonable to use Parallel.ForAsync of .NET 8 or greater.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Investigate Requires further investigation by the WPF team. Priority:3 Work that is nice to have
Projects
None yet
Development

No branches or pull requests

5 participants
0