Hello, there is a goal: to make a certain action in one stream for some "event" from another ... let's say one in an infinite loop sends one packet to the network, and when an "event" occurs in another stream, it starts sending another packet to the network. ..

class Notifier { int atomic_flag = 0; static void sending() { while(1) { if(!atomic_flag) /*одно действие*/ else /*другое действие*/ } } static void observable() { /*...*/ //происходит "событие" atomic_flag=1; /*...*/ } static void Main() { Task task1 = new Task(sending); Task task2 = new Task(observable); task1.Start(); task2.Start(); } } 

Is it possible to do the same thing properly, rather than using a variable with a side effect?

    3 answers 3

    It seems to me that the task contains a contradiction: it is impossible to “not use a variable with a side-effect” to change the behavior when one of the functions is spinning in an infinite loop.

    In this case, you need some shared behavior through which this function will be able to get information about which logic to use.

    One solution is a sparse thread-safe variable. It can be another task, it can be any other variable that allows you to read and write yourself atomically: for example, a volatile variable of reference type.

    Here is one of the task-based options:

     using System; using System.Threading; using System.Threading.Tasks; class Notifier { private static Task<bool> taskThatMeansSomething = Task.FromResult(true); static async Task SendingAsync(CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { bool flag = await taskThatMeansSomething; if (flag) { Console.WriteLine("Flag is true. Doing one thing"); } else { Console.WriteLine("Flag is false. Doing one stuff"); } await Task.Delay(500, cancellationToken); } } static async Task Observable(CancellationToken cancellationToken) { bool result = false; while (!cancellationToken.IsCancellationRequested) { Console.WriteLine($"Changing the flag to {result}"); taskThatMeansSomething = Task.FromResult(result); result = !result; await Task.Delay(1000, cancellationToken); } } static void Main() { var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(5)); Task sendingTask = Task.Factory.StartNew( async () => await SendingAsync(cancellationTokenSource.Token), TaskCreationOptions.LongRunning); Task observeTask = Task.Factory.StartNew( async () => await Observable(cancellationTokenSource.Token), TaskCreationOptions.LongRunning); Console.WriteLine("Waiting 5 seconds..."); Task.WaitAll(sendingTask, observeTask); Console.WriteLine("Done! Press \"Enter\" to exit."); Console.ReadLine(); } } 

    Conclusion:

     Waiting 5 seconds... Flag is true. Doing one thing Changing the flag to False Done! Press "Enter" to exit. Flag is false. Doing one stuff Flag is false. Doing one stuff Changing the flag to True Flag is true. Doing one thing Changing the flag to False Flag is false. Doing one stuff Flag is false. Doing one stuff Flag is false. Doing one stuff Changing the flag to True Flag is true. Doing one thing Flag is true. Doing one thing Changing the flag to False Flag is false. Doing one stuff 

    Well, there are a few moments:

    1. Tasks need to be run via TaskFactory.StartNew and indicate that they are long (because they are long)
    2. It is possible, and even desirable, to tie a graceful cancellation through the CancellationTokenSource and CancellationToken .
    3. It is reasonable to use the standard for C # idiom naming.
    4. It is reasonable to make long running methods return Task .

    Instead of the taskThatMeansSomething variable variable, taskThatMeansSomething can think in the direction of the same TPL Dataflow, when, depending on the input data changes, different blocks are launched that are responsible for sending data in one or another mode.

    Here then it will be possible to fully escape from the shared state in the direction of more actor-based parallelism. If interested, then I can jot down this option too.

    • Please scribble if it's not difficult ... another thing is that with async / await I have a wood-grouse one, I just know what is implemented on the basis of fsm, and that's all) - xperious
    • There I wrote about TPL Dataflow. This is a completely different beast. But I will sketch. - Sergey Teplyakov
    • hmm, i'm wondering: what's the sharna task better than the usual boolean flag? - xperious
    • So that it is thread safe. A boolean flag marked volatile will work too. Without volatile, the compiler (C # and JIT) may optimize such behavior to be completely incorrect. - Sergey Teplyakov

    If I'm not mistaken, then ManuaResetEventSlim is used for such purposes .

    • hmm, well, it blocks the flow of waiting, but it is necessary that it worked in that and in another case - xperious

    I would, frankly, write like this:

     async Task T1(CancellationToken ct) { while (!ct.IsCancellationRequested) { /*одно действие*/ } } async Task T2(CancellationToken ct) { while (!ct.IsCancellationRequested) { /*другое действие*/ } } async Task Coordinator() { using (var cts1 = new CancellationTokenSource()) { var t1 = T1(cts1.Token); /*...*/ //происходит "событие" cts1.Cancel(); await t1; } using (var cts2 = new CancellationTokenSource()) { var t2 = T2(cts2.Token); ... } } 

    The problem is that you use not streams, but streams. No, formally you have a task, but in fact you work with them as if they were streams. therefore, you are thinking about how to start another task in the same task, although this is not necessary in principle.

    Task should not think what stream it is in, and CancellationToken is idiomatic to cancel the task. Well, doing two different things in one task is wrong.

    • bad for me with async / await ... you need to figure it out - xperious
    • @xperious: Be sure to figure it out, this is a key technique in asynchronous tasks. - VladD
    • There is no such thing in the pros, unfortunately, at least at the level of the language, but only in Sharp I ran into it the first time, although I had heard about it a long time ago - xperious
    • @xperious: Maybe producer / consumer will still be useful to you. - VladD
    • Thanks ... - xperious