There is a situation in which it seems that the function is performed 2 times, but in fact it turns out that 2 times it only increases a certain counter. Why this happens to me is not entirely clear. I will cite the blocks of code that may be indirectly related to this function. I simplify the code a bit, removing obviously unimportant moments.

The function in which the counter is increased twice:

void SendedBlock(int counter, int counter2) { PMData _sendData = new PMData(pmd); //..... // Добавляет указанную функцию в пул задач(опишу его в кратце далее) // И именно в передаваемой функции счётчик уменьшается соответвенно PoolForAction.Add(FuncAction, _sendData); // Следующая строка увеличивается 2 раза, но не сразу SendedCounter++; Thread.Sleep(2000); } 

The function in which the counter is subtracted:

  void FuncAction(object _params) { if (_params is MyType) { //...... //Вот тут счетчик вычитается но только 1 раз(далее объясню последовательность) SendedCounter--; } } 

The task in which the execution of this function is called:

  MultyLevelSended MLS = new MultyLevelSended(); void TaskStart() { SendedCounter = 0; MLS.LVL1CancelToken = LocalCancelToken; MLS.LVL2CancelToken = SendedCancelToken.ToArray(); MLS.LVL1Count = Counts; MLS.AUpdate = i => { /// Некая функция в которой я проверяю нужно ли входить на второй уровень цикла. }; MLS.Run(SendedBlock); } 

MultyLevelSended: (just some kind of wrapper, maybe it plays a role)

 public class MultyLevelSended { public int LVL1Count = 0; public int LVL2Count = 0; public CancellationTokenSource LVL1CancelToken = null; public CancellationTokenSource[] LVL2CancelToken = null; public Action<int> AUpdate = null; public void Run(Action<int,int> _aMain) { try { Task.Factory.StartNew(() => { for (int i = 0; i < LVL1Count; i++) { AUpdate?.Invoke(i); try { Task.Factory.StartNew(() => { for (int j = 0; j < LVL2Count; j++) { _aMain?.Invoke(i,j); } }, LVL2CancelToken[i].Token).Wait(); } catch { } } }, LVL1CancelToken.Token).Wait(); } catch { } } } 

PoolForAction.Add() in fact simply calls the Post() function for the Dataflow block, in which in fact all submitted functions are executed in turn.

So here. TaskStart launching the TaskStart function (which was previously called via Task.Run as a task), the counter is reset. then all the necessary moments are filled in for the cycle wrapper and it is actually launched for execution. Suppose that the main transferred function is executed 2 times (the second level of the counter will be equal to 2 m). It will be executed 1 time, increasing the counter accordingly 1 time and sending it to the Poole a function that reduces it by 1 when it is executed, it will also happen a second time. But after that (focusing on breakpoints) he will return to SendedCounter++ and will simply increase it 2 times in a row. Logically, if he increases the second time, then he must send the event to the pool for execution a second time, and the counter should decrease, but he does not do this, but only increases the 2d counter. What is the problem to understand I did not succeed. I would be grateful for the help.

UPD:
I slightly added a wrapper for the test, so that after exiting the loop, the called function becomes Null and is not executed:

 try { test = _aMain; Task.Factory.StartNew(() => { for (int j = 0; j < LVL2Count; j++) { test?.Invoke(i,j); } test = null; }, LVL2CancelToken[i].Token).Wait(); } catch { } 

But it does not help. The code comes to test=null , and then returns back to test?.Invoke(i,j); and it is executed anyway, because again it is not equal to null

UPD 2:

I changed the code and removed the tasks from it, and wrote:

  public void Run(Action<int, int> _aMain) { for (int i = 0; i < LVL1Count; i++) { if (!LVL1CancelToken.IsCancellationRequested) { if (AUpdate?.Invoke(i) ?? true) for (int j = 0; j < LVL2Count; j++) { if (!LVL2CancelToken[i].IsCancellationRequested) { Console.WriteLine("Прохождения внутреннего цикла " + j + " Внешний " + i); _aMain?.Invoke(i, j); } } } } } 

In order to verify the cause of the problem. But in this and that case, the scheme remains the same, the function is called twice, and already with the following index. Visually how this happens is all I portrayed in this image: enter image description here

As described above, this "overshoot" appears only if run without debugging , if you start and check it step by step, then in 98% of cases these 2 extra calls will not be.

  • I'm not a C # programmer, but do you have a SendedCounter that is a variable or class parameter? and only in one function he (she) increment? - Naumov
  • @Naumov SendedCounter is a local variable in the class. Yes. only in one. All names in the specified functions - BwehaaFox
  • one
    You have a complicated code, however. I would suggest temporarily turning the SendedCounter into a property, then you can set a breakpoint on the setter, and you will see who changes it a second time. - VladD
  • @VladD I put the output to the console when entering the zoom function and the zoom function. In fact, it turned out that a double entry into the zoom function actually happens at some point, but the function with decreasing toli is not delivered to the pool, toli for some reason is ignored, but again the point is that for some reason the function is called twice and not in a row, but it is called only in the MultyLevelSended loop. I am on the basis of the log was about this sequence. Screen - BwehaaFox 2:57 pm
  • @VladD these 2 function calls occur very strange. That is, according to this scheme, which I wrote, AUpdate "gives the go-ahead" at the entrance to the second loop level for the element with index 1 . after which, in turn, it calls the main function ( _aMain ) in the loop, after which, even before exiting this cycle, the main function is called 2 more times, but already for the element with index 2 , that is, before checking via AUpdate and before changing the counter , or rather immediately after the first 2x. - BwehaaFox

2 answers 2

If this is a multi-threaded code, you should use Mutex. And the easiest way to understand how to increase, put a breakpoint and see the call on a glass trace.

    The answer is found:
    In fact, this problem did not even arise because of the desynchronization of threads or because of something like that. The whole point was in a different piece of code.

    In order:
    I used TransformBlock from TPL Dataflow to perform some tasks. There was a function to add a task to the queue, but later I needed to do a similar function that stopped the thread in which it was called until the sent code block was executed, that is, there was some kind of wait.

    Initially, the functions had the following form (approximately):

     public void Add(Action<object> func, PMData data) { ActionParams _pack = new ActionParams(); /// всякие инициализации ActionBlock.Post(_pack); ActionAddedEvent?.Invoke(_pack); } public void AddWithAwait(Action<object> func, PMData data) { ulong AwaitIndex = _poolIndex + 1; Add(func, data); while (ActionBlock.Receive() != AwaitIndex) { Thread.Sleep(10); } } 

    In general, it worked, but the problem of the "Secondary phantom" function call, as I understood it, lay on the shoulders of ActionBlock.Receive() if in the pool block and in the task sending block to the pool, there was an identical wait. In my case, the pool was processed with a delay of 2 seconds, and the blocks were sent with the same delay, which might have forced the waiting block to execute the toli twice, the toli like that, even before calling it again and somehow checking through the function. I also noticed that such a problem arose only during the runtime execution without bricpoint and without stepwise execution. Changing the code for the next, the problem was solved.

      public void Add(Action<object> func, PMData data) { ActionParams _pack = new ActionParams(); ActionBlock.Post(_pack); AwaitebleTotalIndex++; ActionAddedEvent?.Invoke(_pack); } public void AddWithAwait(Action<object> func, PMData data) { Add(func, data); ulong AwaitIndex = AwaitebleTotalIndex; while (_poolIndex<AwaitIndex) { Thread.Sleep(10); } }