About timer.Elapsed leads to abnormal execution order

Problem Description:

When doing a server connection test, in order to detect message reception and reply, the following code is used to regularly detect information.

			var timer = new System.Timers.Timer(1000.0 / 20);         
            timer.Elapsed += (object sender, ElapsedEventArgs e) =>
            {
    
       
                if (_socket.Connected)
                {
    
    
                    _socket.Tick(1);
                }                        
            };
            timer.AutoReset = true;
            timer.Enabled = true;
public int Tick(int processLimit, Func<bool> checkEnabled = null)
        {
    
    
            // only if state was created yet (after connect())
            // note: 我们不会选中“仅在已连接的情况下”,因为我们也想在以后继续处理“断开连接”消息!
            if (state == null)
                return 0;

            // process up to 'processLimit' messages
            for (int i = 0; i < processLimit; ++i)
            {
    
    
                // 如果启用了“镜像”场景消息,则启用此检查
                if (checkEnabled != null && !checkEnabled())
                    break;

                // 先偷看。 允许我们处理第一个排队的条目,同时通过不删除任何内容来保持池中的byte []有效。
                if (state.receivePipe.TryPeek(out int _, out EventType eventType, out ArraySegment<byte> message))
                {
    
    

                    switch (eventType)
                    {
    
    
                        case EventType.Connected:
                            OnConnected?.Invoke();
                            break;
                        case EventType.Data:
                            OnData?.Invoke(message);
                            break;
                        case EventType.Disconnected:
                            OnDisconnected?.Invoke();
                            break;
                    }
                    //重要说明:现在,在完成处理事件之后,将队列从队列中取出并返回到池中。
                    state.receivePipe.TryDequeue();
                }
                // no more messages. stop the loop.
                else break;
            }
            // 返回下一次要处理的内容
            return state.receivePipe.TotalCount;
        }

Then it appeared that some code logic in my tick was chaotic, and theoretically it was executed sequentially from top to bottom. But the actual situation is that OnData?.Invoke(message); is executed multiple times; and then state.receivePipe.TryDequeue(); is performed to remove the queue.

solution

It may be that I am not familiar with these things, and the reason is still unclear after many tests.
From the performance point of view, I performed multiple operations before removing the queue. But if the logic is executed in order.
Executed for the first time OnData?.Invoke(message);.
The information will be removed and state.receivePipe.TryDequeue();
will not be detected the next time it enters the method detection state.receivePipe.TryPeek(out int _, out EventType eventType, out ArraySegment<byte> message).

So try to fix it as follows

    if (state.receivePipe.TryPeek(out int _, out EventType eventType, out ArraySegment<byte> message))
                {
    
    
                    //重要说明:现在,在完成处理事件之后,将队列从队列中取出并返回到池中。
                    state.receivePipe.TryDequeue();
                    switch (eventType)
                    {
    
    
                        case EventType.Connected:
                            OnConnected?.Invoke();
                            break;
                        case EventType.Data:
                            OnData?.Invoke(message);
                            break;
                        case EventType.Disconnected:
                            OnDisconnected?.Invoke();
                            break;
                    }
                }

This indeed solves the problem I posed.

But because this is the use of other people's plug-in packages, and many people use it again and no one has raised the above questions, so I still feel that I don't want to change other people's source code to avoid other problems.

Then I tried to look at the description of this timer.Elapsed.
insert image description here
Then the red box part is what I think may cause this kind of problem.
This is related to threads. Of course, as Unity development, it is basically far away from this concept, because Unity only has the main thread.
So I think it should be fine to set this property, but the case has only one Form class as its object, and I don't know how to do it after many attempts. (There are big guys who have corresponding usage methods in this area, so please comment.)

Then I used another method
as follows

 			var timer = new System.Timers.Timer(1000.0 / 20);        
            timer.Elapsed += (object sender, ElapsedEventArgs e) =>
            {
    
    
                lock (this)
                {
    
    
                    if (_socket.Connected)
                    {
    
    
                        _socket.Tick(1);
                    }
                }                                   
            };
            timer.AutoReset = true;
            timer.Enabled = true;

When performing detection, lock it to avoid the processing of the event. If the Elapsed duration exceeds the Interval, the event may be triggered again on another thread ThreadPool.
This also solves the problem.

About some special attempts to record

In fact, among the above solutions, I also tried another solution. That is, they all operate on the same thread. Then the loop does not use timer.Elapsed but uses while(true).
The test result is of course no problem, but the problem is that while(true) can only be executed as the last method in the same thread. This still bothered me for a few minutes. I have done a lot of things, but I am a little lost in simple things.

that's all.

Guess you like

Origin blog.csdn.net/qq_39860954/article/details/117226193