Closed Whiletru3 closed 2 years ago
I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.
Hello, can you reduce this repro further?
I reduced it to the minimum :
using System;
using System.Threading;
using System.Threading.Tasks;
namespace multithread
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
// prepare the thing to do in multithread
int npages = 10;
var indexesFinished = new bool[npages];
var task = new Task(() =>
{
Parallel.For(0, npages, new ParallelOptions { MaxDegreeOfParallelism = 2 }, (index) => {
// simulate work to do ...
Thread.Sleep(1000);
indexesFinished[index] = true;
Console.WriteLine($"Convert page {index} - ${Thread.CurrentThread.Name}");
});
});
task.Start();
// this loop is responsible for the lock/freeze
// ok this way to do is not a best practice, but it shouldn't lock/freeze
// if you run it, it will lock/freeze.
// if you put a breakpoint in the first line in the while, and do a step by step it will go to the end
// if you uncomment the Thread.Sleep(1), and run, il will go to the end.
int i = 0;
while (i < npages)
{
if (indexesFinished[i])
{
Console.WriteLine($"Create page {i}");
i++;
}
else
{
//Thread.Sleep(1);
}
}
task.Wait();
Console.WriteLine("finished");
}
}
}
Tagging subscribers to this area: @JulieLeeMSFT See info in area-owners.md if you want to be subscribed.
Author: | Whiletru3 |
---|---|
Assignees: | - |
Labels: | `area-CodeGen-coreclr`, `untriaged` |
Milestone: | - |
@echesakovMSFT PTAL. cc @dotnet/jit-contrib (corrected echesakov with echesakovMSFT)
@Whiletru3 I believe such behavior (in your last example) is correct, there is no guarantee that the value of indexesFinished[i]
read by Main
function thread would be the latest value (i.e. true
) as set by the other (worker) threads. Hence, the loop in Main
never finishes because the following branch is never taken
if (indexesFinished[i])
{
i++
}
@Whiletru3 why do you need the while loop at all -- can you move that work into each of the worker threads instead?
More generally, can you eliminate Parallel.For
and Thread
and just use Task
based programming throughout? then the system will handle scheduling, and you can chain work with ContinueWith()
and wait on everything with WaitAll()
.
@echesakovMSFT
@Whiletru3 I believe such behavior (in your last example) is correct, there is no guarantee that the value of
indexesFinished[i]
read byMain
function thread would be the latest value (i.e.true
) as set by the other (worker) threads. Hence, the loop inMain
never finishes because the following branch is never takenif (indexesFinished[i]) { i++ }
I have things to do in parallel then get the results in the original order. This code is working well on Windows but hang on mac. Of course, the last loop is not a best practice (as it is running and take cpu in the loop), but it should not hang.
In the first repro, i use threads and blockingcollection, so the workers take work in the order of the blockingcollection. The second repro is only to isolate the issue, parallel.for is taking work in random order that's why i dont use that...
@danmoseley
@Whiletru3 why do you need the while loop at all -- can you move that work into each of the worker threads instead?
More generally, can you eliminate
Parallel.For
andThread
and just useTask
based programming throughout? then the system will handle scheduling, and you can chain work withContinueWith()
and wait on everything withWaitAll()
.
I can't use the ContinueWith and WaitAll as only one thread should take the result of the worker in the right original order. Basically, here i need to rasterize pdfs to images and get the pages in the right order to display in a viewer.
The Tasks can return an index that you can sort on?
@stephentoub is there a canonical way to do this?
is there a canonical way to do this?
From skimming the thread, the request is basically a way to process a sequence in parallel but consume the results serially in the original order as they're available?
That necessitates a reordering buffer that can track completed results by original index and yield the next one in the original order when it's available. Both PLINQ and the Dataflow library include such support.
With PLINQ, it might look like:
IEnumerable<int> output = from i in ParallelEnumerable.Range(0, 100).AsOrdered().WithMergeOptions(ParallelMergeOptions.NotBuffered).WithDegreeOfParallelism(2)
select Process(i);
foreach (int item in output)
{
Console.WriteLine(item);
}
static int Process(int input)
{
// Simulate some work
Thread.Sleep(Random.Shared.Next(1, 1000));
return input * 2;
}
With Dataflow, it might look like:
using System.Threading.Tasks.Dataflow;
var transform = new TransformBlock<int, int>(input => Process(input), new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 2 });
for (int i = 0; i < 100; i++)
{
transform.Post(i);
}
await foreach (int item in transform.ReceiveAllAsync())
{
Console.WriteLine(item);
}
static int Process(int input)
{
// Simulate some work
Thread.Sleep(Random.Shared.Next(1, 1000));
return input * 2;
}
@Whiletru3 Do you see any output before the app hangs?
@Whiletru3 Do you see any output before the app hangs?
@echesakovMSFT : only the Helloworld, then it freeze
@Whiletru3 Do you see any output before the app hangs?
@echesakovMSFT : only the Helloworld, then it freeze
How do you launch your sample, does hang reproduce via dotnet run -c Release -f net5.0
?
I launch it in debug inside visual studio mac. Launching it in release inside visual studio freeze Launching it in release inside visual studio don't freeze dotnet run -c Release -f net5.0 don't freeze dotnet run -c Debug -f net5.0 don't freeze
@Whiletru3 Based on the output you are observing the workers threads never start executing. And this only happens under VS for Mac, not with "dotnet run" command.
I looked at the JIT-ed code for both Main
and the worker methods and haven't seen anything suspicious. In order to look into this further I would need someone who has macOS x64. @BruceForstall Can you please try reproducing this on your Mac?
@Whiletru3 Based on the output you are observing the workers threads never start executing. And this only happens under VS for Mac, not with "dotnet run" command.
@echesakovMSFT : that's right
@BruceForstall, opportunistically setting to .NET 7. Please move to future if it cannot be done in .NET 7.
I can reproduce this hang with the original example above on VS for Mac 17.3 (Build 2102) (on x64). However, the "hang" does not occur when using "dotnet run" at the console. Given this, it is not a .NET runtime issue, and probably not even a libraries issue. Perhaps there is some problematic way in which VS for Mac invokes applications? Or perhaps there is some illegal threading-related code in the test case, exacerbated by the VS for Mac invocation.
If this is still a problem for you, I suggest opening a bug in Visual Studio for Mac ("Help" => "Report a Problem").
Description
Hello,
I have a lock/freeze on dotnet using Threads, BlockingCollection and a while loop. If I put wait in the last loop, it will not freeze/lock This lock happened in the latest stable Visual Studio on Mac : 8.10.10 (build 8)
Reproduction Steps
Expected behavior
the process should go to the end and write in the console log Like this : Hello World! Convert page 1 - $Thread_FileConverter 0 Convert page 2 - $Thread_FileConverter 3 Convert page 0 - $Thread_FileConverter 1 Convert page 3 - $Thread_FileConverter 2 Create page 0 Create page 1 Create page 2 Create page 3 Convert page 4 - $Thread_FileConverter 0 Convert page 6 - $Thread_FileConverter 1 Convert page 5 - $Thread_FileConverter 3 Convert page 7 - $Thread_FileConverter 2 Create page 4 Create page 5 Create page 6 Create page 7 Convert page 8 - $Thread_FileConverter 0 Create page 8 Convert page 9 - $Thread_FileConverter 1 Create page 9 finished
Actual behavior
this code lock/freeze the dotnet process run at 100% of cpu, break in visual studio do nothing, and stop keep the 100%cpu dotnet process running
Regression?
No response
Known Workarounds
put a thread.sleep in the last while resolve the freeze/lock
Configuration
=== Visual Studio Community 2019 for Mac ===
Version 8.10.10 (build 8) Installation UUID: a3923c5c-dae0-47f1-a41f-fef3c1dd9e5e GTK+ 2.24.23 (Raleigh theme ) Xamarin.Mac 6.18.0.23 (d16-6 / 088c73638)
=== Mono Framework MDK ===
Runtime: Mono 6.12.0.140 (2020-02/51d876a041e) (64-bit) Package version: 612000140
=== Roslyn (Language Service) ===
3.10.0-4.21269.26+029847714208ebe49668667c60ea5b0a294e0fcb
=== NuGet ===
Version: 5.9.0.7134
=== .NET Core SDK ===
SDK: /usr/local/share/dotnet/sdk/5.0.401/Sdks SDK Versions: 5.0.401 5.0.400 3.1.413 3.1.402 MSBuild SDKs: /Applications/Visual Studio.app/Contents/Resources/lib/monodevelop/bin/MSBuild/Current/bin/Sdks
=== .NET Core Runtime ===
Runtime: /usr/local/share/dotnet/dotnet Runtime Versions: 5.0.10 5.0.9 3.1.19 3.1.8 2.1.23 2.1.22
=== Updater ===
Version: 11
=== Apple Developer Tools ===
Xcode 11.6 (16141) Build 11E708
=== Build Information ===
Release ID: 810100008 Git revision: a3ff4b6e658e1f94623e1f3ed34ca94ed4fe78d8 Build date: 2021-09-23 19:50:51-04 Build branch: release-8.10
=== Operating System ===
Mac OS X 10.15.6
Other information
No response