In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-18 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >
Share
Shulou(Shulou.com)06/01 Report--
This article mainly introduces. Net framework how to achieve high-precision timer, has a certain reference value, interested friends can refer to, I hope you can learn a lot after reading this article, the following let the editor take you to understand it.
1 background
The .NET Framework provides four timers, but none of them are accurate (usually around 15ms), so it is difficult to meet the requirements in some scenarios.
Timers with less than 10ms precision may be required for media playback, animation, performance analysis, and interaction with hardware. Whether this demand is reasonable or not is not discussed here, it is a real problem, and there are quite a lot of places under discussion, which shows that it is a real demand. However, it is not easy to achieve it.
This does not involve the timer at the kernel driver level, but only analyzes the implementation of the high-precision timer at the application level in the .NET managed environment.
Windows is not a real-time operating system, so any scheme can not absolutely guarantee the accuracy of the timer, but can reduce the error as much as possible. Therefore, the stability of the system can not completely depend on the timer, and the processing when losing synchronization must be considered.
2 waiting strategy
In order to achieve high-precision timer, it is necessary to have two basic functions: waiting and timing. Waiting is used to skip a certain time interval, and timing can be checked to adjust the waiting time.
There are actually two waiting strategies:
Spin wait: letting CPU idle consumes time and takes up a lot of CPU time, but the time is highly controllable.
Blocking wait: the thread enters the blocking state, transfers the CPU time slice, waits for a certain amount of time, and then is dispatched back to the running state by the operating system. Blocking does not take up CPU, but it needs operating system scheduling, and the time is difficult to control.
We can see that both of them have their own advantages and disadvantages, and should be implemented differently according to different needs.
It can be said that only one kind of timing mechanism can be used, that is, the Stopwatch class. It internally uses the system API QueryPerformanceCounter/ QueryPerformanceFrequency for high-precision timing, which depends on hardware, and its accuracy can be as high as tens of nanoseconds, so it is very suitable for realizing high-precision timers.
So the difficulty lies in the waiting strategy. Let's first analyze the simple spin waiting.
2.1 spin wait
You can use Thread.SpinWait (int iteration) to spin, that is, to make CPU idle in a loop, and the iteration parameter is the number of iterations. It is used in many synchronous constructs in .NET Framework to wait for a short period of time and reduce the overhead of context switching.
It is difficult to calculate the elapsed time based on iteration, because the CPU speed can be dynamic. So you need to use Stopwatch in combination. The pseudo code is as follows:
Var wait start time = current timing; while ((current timing-waiting start time) < waiting time) {spin;}
Write the actual code:
Void Spin (Stopwatch w, int duration) {var current = w.ElapsedMillisecondPositive while ((w.ElapsedMilliseconds-current) < duration) Thread.SpinWait (10);}
W here is a started Stopwatch, to demonstrate the simple use of the ElapsedMilliseconds attribute, the precision is millisecond, using the ElapsedTicks attribute can achieve higher precision (microsecond level).
However, as mentioned earlier, this precision is high but at the expense of CPU time, so implementing timers will make a CPU core work at full load (if the tasks performed are not blocked). It is equivalent to wasting a core, which is not very realistic in some cases (such as virtual machines with few or even single cores), so you need to consider blocking waiting.
2.2 blocking waiting
Blocking wait gives control to the operating system, so it is necessary to ensure that the operating system can schedule the timer thread back to running state in a timely manner. By default, Windows's system timer precision is 15.625ms, which means that the time slice is this size. If a thread is blocked, its time slice is assigned to wait, and the time to be scheduled to run is at least one slice 15.625ms. Then the length of the time slice must be reduced in order to achieve higher accuracy.
You can modify the system timer precision to 1ms through the system API timeBeginPeriod (it internally uses an undocumented NtSetTimerResolution, and this API can be modified to 0.5ms). Use timeEndPeriod restore when you don't need it.
Modifying the accuracy of the system timer has side effects. It will increase the cost of context switching, increase power consumption, and reduce the overall performance of the system. However, many programs have to do this because there is no other way to achieve higher timer accuracy. For example, WPF-based programs (including Visual Studio), applications using Chromium kernel (Chrome, QQ), multimedia players, games and many other programs will change the system timer accuracy to 1ms within a certain period of time. (for viewing method, see below)
So in fact this side effect has become the norm in the desktop environment. And since Windows 8, this side effect has abated.
Under the premise of 1ms's system timer accuracy, blocking wait can be implemented in three ways:
Thread.Sleep
WaitHandle.WaitOne
Socket.Poll
In addition, the multimedia timer timeSetEvent also uses blocking.
Thread.Sleep
Its parameters are in millisecond units, so it can only be accurate to 1ms at most. However, it is actually very unstable. Thread.Sleep (1) will jump between 1ms and 2ms, that is, it may produce more errors of + 1ms.
It is found that when there is no task load (pure loop call Sleep (1)), the blocking duration is stable at 2ms, while when there is a task load, it will block 1ms at least. This is different from the other two blocking methods, as detailed below.
If you need to correct this error, you can use Sleep (NMel 1) when blocking n milliseconds, and timing through Stopwatch, and the remaining wait time is supplemented by Sleep (0), Thread.Yield, or spin.
Sleep (0) transfers the remaining CPU time slices to threads with the same priority, while Thread.Yield allocates the remaining CPU time slices to threads running on the same core. At the end of the transferred time slice, it will be rescheduled. In general, the whole process can be done within 1ms.
Thread.Sleep (0) and Thread.Yield are very unstable in the case of high CPU load, and the measured data may block up to 6ms time, so more errors may be generated. Therefore, error correction is best achieved by spin.
WaitHandle.WaitOne
WaitHandle.WaitOne is similar to Thread.Sleep in that the parameters are in millisecond units.
The difference is that when there is no task load (pure loop call WaitOne (1)), the blocking duration is stable at 1.5ms, while when there is a task load, it may only block for a time close to zero (guess that it only blocks until the end of the current time slice, no specific documentation has been found). So the duration of its blocking ranges from 0 to more than 2ms.
WaitHandle.WaitOne (0) is used to test the state of the wait handle, and it does not block, so using it for error correction is similar to spin, but not as reliable as using spin directly.
Socket.Poll
The parameter of Socket.Poll method is in microseconds. In theory, it uses the hardware of network card to set the time, and the precision is very high. However, because the implementation of blocking still depends on threads, it can only achieve the precision of 1ms.
Its advantage is that it is more stable and has less error than Thread.Sleep and WaitHandle.WaitOne. It does not need to be corrected, but it takes up a Socket port.
In the case of no task load (pure loop call Poll (1)), the blocking duration is stable at 1ms, while when there is a task load, similar to WaitOne, it may only block for a time close to zero. So the duration of its blocking ranges from 0 to more than 1ms.
Socket.Poll (0) is used to test the state of Socket, but it can block and may block up to 6ms, so it cannot be used for error correction.
TimeSetEvent
TimeSetEvent belongs to the multimedia timer function provided by winmm.dll as well as timeBeginPeriod mentioned earlier. It can be used directly as a timer and also provides the precision of 1ms. Use timeKillEvent to close when you don't need it.
Its stability and accuracy are also very high, if you need the timing of 1ms, but can not use spin, then this is the most ideal solution.
Although MSDN says that timeSetEvent is an outdated method, it should be replaced with CreateTimerQueueTimer. However, CreateTimerQueueTimer is not as accurate and stable as multimedia timers, so when high precision is needed, only timeSetEvent can be used.
3 timer implementation
It is important to note that whether spinning or blocking, it is clear that timers should run on separate threads and should not interfere with the work of the user thread. For high-precision timers, the threads that trigger events to execute tasks are generally in the timer thread, rather than using separate task threads.
This is because in a high-precision timing scenario, the time cost of executing a task is likely to be greater than the interval of the timer, which may take up a large number of threads if the task is executed in other threads by default. Therefore, control should be given to the user, allowing the user to schedule the thread for task execution when needed.
3.1 trigger mode
Because the task is executed on the timer thread, the trigger of the timer produces three modes. Here are their descriptions and the main loop pseudocode:
Fixed time frame
For example, interval 10ms, task 7-12ms, will follow wait 10ms, task 7ms, wait 3ms, task 12ms (timeout 2ms loses synchronization), task 7ms, wait 1ms (return to synchronization), task 7ms, wait for 3ms, … Proceed. Is to try to execute the task according to the set time frame, as long as the task does not always time out, you can return to the original time frame.
Var next frame time = 0 position while (timer on) {next frame time + = interval time; while (current timing < next frame time) {wait;} trigger task;}
Deferrable time frame
In the above example, wait 10ms, task 7ms, wait 3ms, task 12ms (timeout, deferred time frame 2ms), task 7ms, wait 3ms, … Proceed. Timed-out tasks delay the time frame.
Var next frame time = 0 Bing while (timer on) {next frame time + = interval time; if (next frame time < current timing) next frame time = current timing while (current timing < next frame time) {wait;} trigger task;}
Fixed waiting time
In the above example, wait 10ms, task 7ms, wait 10ms, task 12ms, wait 10ms, task 7ms... Proceed. The waiting time remains the same.
While (timer start) {var wait start time = current timing; while ((current timing-waiting start time) < interval time) {wait;} trigger task;} / or: var next frame time = 0 position while (timer starts) {next frame time + = interval time; while (current timing < next frame time) {wait;} trigger task; next frame time = current timing;}
If you use a multimedia timer (timeSetEvent), it always implements the first mode, while the other waiting strategies can implement all three modes, which can be selected according to your needs.
Waits in while loops can use spin or blocking, or they can be combined to achieve a balance of precision, stability, and CPU overhead.
In addition, as can be seen from the pseudo code above, the implementation of the three modes can be unified and can be switched according to the situation.
3.2 Thread priority
It is best to raise the thread priority to ensure that the timer works steadily and reduce the chance of being preempted. It is important to note, however, that this can lead to hunger of low-priority threads when CPU resources are insufficient. In other words, the high-priority thread cannot be allowed to wait for the low-priority thread to change its state, and it is very likely that the low-priority thread does not have a chance to run, resulting in a deadlock or a deadlock-like state. (see an example of a similar hunger)
The final priority of a thread is related to the priority of the process, so it is sometimes necessary to increase the process priority (see the thread priority description of the multithreaded series in C#).
4 other
There are two more points to note:
Thread safety: the timer runs on a separate thread, and its exposed members should be thread-safe, otherwise calling the timer while the timer is running may cause problems.
Timely release of resources: multimedia timers, wait handles, threads, and so on are all system resources that should be released / destroyed when they are not needed.
How do I check the accuracy of the system timer?
For a simple view, you can use ClockRes in the Sysinternals toolkit, which displays the following information:
Maximum timer interval: 15.625 msMinimum timer interval: 0.500 msCurrent timer interval: 15.625 ms// or Maximum timer interval: 15.625 msMinimum timer interval: 0.500 msCurrent timer interval: 1.000 ms
If you want to see which programs request higher system timer precision, run:
Powercfg energy-duration 5
It will monitor the system energy consumption for 5s and then generate an energy-report.html analysis report in the current directory, which you can open to view.
If you find the warning section inside, there will be platform timer resolution: outstanding timer request (Platform Timer Resolution:Outstanding Timer Request) information.
Thank you for reading this article carefully. I hope the article "how to achieve a high-precision timer with .net framework" shared by the editor will be helpful to you. At the same time, I also hope that you will support us and pay attention to the industry information channel. More related knowledge is waiting for you to learn!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.