0
var a = 3
var b= 5
for (int i = 0; i < 10; i++)
{
    var c = a + b;
    Console.WriteLine(c); //This line should execute on another thread
}

How could I make the value of c print on another thread, without making a new thread each iteration? The reason I want to make it run on another thread is because writing the value of c would block the thread and make the script slower. Basically its for visual feedback

Note: I cant put a loop in the thread since the thread is only supposed to run after c is calculated

electro
  • 55
  • 5
  • 1
    You should put the loop into the thread. – Soner from The Ottoman Empire Sep 11 '22 at 12:40
  • @snr i need the thread to run only after the first line is complete, if i put the loop inside the thread, the thread would run regardless of the first line being completed or not – electro Sep 11 '22 at 12:44
  • @electro Do you need it to wait until `Console.WriteLine("hello world")` has finished before proceeding to the next iteration? – Andrew Morton Sep 11 '22 at 12:45
  • So, basically, you want two threads, and you want the first thread to "trigger" the second thread to do something. Did I get that right? If yes, you need to *synchronize* the threads. You can do that, for example, using [AutoResetEvent](https://stackoverflow.com/a/4486637/87698). – Heinzi Sep 11 '22 at 12:46
  • @AndrewMorton That is correct, I want the second thread to run only when the Console.WriteLine("hello world") has finished – electro Sep 11 '22 at 12:49
  • @electro What I meant was, can it do `Console.WriteLine(3)` while `Console.WriteLine("hello world")` is still running from the iteration with `i == 2` (etc.)? – Andrew Morton Sep 11 '22 at 12:51
  • 1
    What are you trying to achieve by running that line in a thread by itself? It seems like a very poor candidate for a multi-threaded workload. – kalleguld Sep 11 '22 at 12:51
  • @Heinzi Not exactly, the main purpose of the second thread is to get visual feedback. For example I have a script inside a for loop that adds 2 numbers. I then need to write the sum. However, writing the sum blocks the thread which slows down the program so making another thread for this would be ideal – electro Sep 11 '22 at 12:51
  • @kalleguld This is just an example, read the comment I wrote to Heinzi – electro Sep 11 '22 at 12:52
  • @AndrewMorton Its not supposed to do that – electro Sep 11 '22 at 12:53
  • @electro In light of your comment to Heinzi, that's an ideal job for a [BackgroundWorker](https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.backgroundworker) - except you run the code in the BGW and use the main thread for the UI. – Andrew Morton Sep 11 '22 at 12:56
  • @electro: Ah, ok. In that case, the canonical solution is to (a) do the computation in a background thread and (b) schedule the output in the UI thread by using `Dispatcher.BeginInvoke` (WPF) or `Control.BeginInvoke` (WinForms). Not sure how to do this in a Console application, though. – Heinzi Sep 11 '22 at 12:56
  • @AndrewMorton would this work for console applications? I'm not using winforms – electro Sep 11 '22 at 12:58
  • An alternative would be to (a) run the computation in a background thread, (b) pass a `Progress` to the background thread, allowing it to report its progress, and store that progress in a variable and (c) in the main thread to regularly (every x ms) print the output. This means that not *every* status report will be written, but it "throttles" the output, preventing it from slowing down your computation. This is useful, for example, if the output is just a percentage (where it doesn't matter if one is skipped). – Heinzi Sep 11 '22 at 13:00
  • @electro It appears that user Maxim Paperno has [an answer you might like](https://stackoverflow.com/a/71182765/1115360). – Andrew Morton Sep 11 '22 at 13:03
  • Sounds like you might just want `Task.Run` but exactly why you want that is unclear. – Charlieface Sep 11 '22 at 13:19

2 Answers2

4

I think you need a standard Producer/Consumer pattern.

using System.Threading.Channels;

Channel<int> channel = Channel.CreateUnbounded<int>();
ChannelWriter<int> writer = channel.Writer;
ChannelReader<int> reader = channel.Reader;

var task = Task.Run(async () => await Consume());

var a = 3;
var b = 5;
for (int i = 0; i < 10; i++)
{
    var c = a + b + i;
    await writer.WriteAsync(c);
}
writer.Complete();

await task; // or Console.ReadKey();

async Task Consume()
{
    await foreach (int x in reader.ReadAllAsync())
    {
        Console.WriteLine(x);
    }
}

The part of the code that publishes data to the channel can also be executed in a separate thread, if necessary.

Alexander Petrov
  • 13,457
  • 2
  • 20
  • 49
0

Based on the information you have given, a possible solution would be the following:

int sum = 0;
object lockObj = new object();

var tokenSource2 = new CancellationTokenSource(); 
CancellationToken ct = tokenSource2.Token;

void Progress()
{
    while (true)
    {
        // We should lock only if writting to the sum var but this is just an example.
        lock (lockObj)
        {
            Console.WriteLine(sum);
        }
        // Just simple wait in order to not write constantly to the console.
        Thread.Sleep(100);
        // If process canceled (finished), then exit.
        if (ct.IsCancellationRequested)
            return;
    }
}

var a = 3;
var b = 5;

Task? t = null;
for (int i = 0; i < 100000; i++)
{
    // Lock to be sure that there is no concurrency issue with the Progress method if it writes to the variable.
    // In our case it shouldn't be required as it only reads.
    lock (lockObj)
    {
        sum = a + b;
    }
    if (t == null)
        t = Task.Run(() => Progress());
}
tokenSource2.Cancel();

t.Wait();

Things to note:

  1. We lock the access of the sum variable although it is not required as we only read from it. You can safely remove the lock statements if you do not intent to write to it from the background thread.
  2. We use a cancellation token to indicate the termination of the thread that does the calculation (in our case the main thread). This way the other thread will terminate correctly and it will not end up running an endless loop.
  3. We use a Task.Wait() after we end our calculation. If we don't do it, the console app will instantly kill the background thread and we might not get results displayed on the screen.