Double buffering queue to reduce the competition for locks
In the daily development, log records are essential. But we also know that writing logs for the same text can only be written in a single thread, so we often use a simple lock lock to ensure that only one thread writes the log information. However, in the multi-threaded to write log information, because the logging information is required for I/O interaction, resulting in the time we occupy the lock longer, resulting in a large number of threads blocking and waiting.
In this scenario, we think about what we can do to make sure that when there are multiple threads to write the log, we are able to queue them in sequence without using the lock. This time we can consider using a double buffer queue to complete.
The so-called double-buffered queue is two queues, one is dedicated to the data write, the other is specifically responsible for data reading, when the logical thread reads the data, it is responsible for the queue of the I/O thread to Exchange.
How do we use this buffer queue to do what we want?
When there are multiple threads writing the log, this time we want to write the information in the queue we are responsible for writing, and then the logical read line Cheng to non-blocking. The logical-read thread is now ready to start working. (The queue at the beginning of the logical read is empty) after the logical read thread reads the data from his own queue (and executes some logic), the reference to the logical read queue and the queue responsible for the write are exchanged for reference. This is a simple idea of a double-buffered queue implementation. The specific implementation code is as follows:
User
public class Doublequeue {private concurrentqueue<user> _writequeue; Private concurrentqueue<user> _readqueue; private volatile concurrentqueue<user> _currentqueue; Private AutoResetEvent _dataevent; Private ManualResetEvent _finishedevent; Private ManualResetEvent _producerevent; Public Doublequeue () {_writequeue = new concurrentqueue<user> (); _readqueue = new concurrentqueue<user> (); _currentqueue = _writequeue; _dataevent = new AutoResetEvent (false); _finishedevent = new ManualResetEvent (true); _producerevent = new ManualResetEvent (true); Task.Factory.StartNew (() = Consumerqueue (), taskcreationoptions.none); public void Producerfunc (user user) {_producerevent.waitone (); _finishedevent.reset (); _currentqueue.enqueue (user); _dataevent.set (); _finishedEvent.set (); } public void Consumerqueue () {concurrentqueue<user> consumerqueue; User user; int allcount = 0; Stopwatch watch = Stopwatch.startnew (); while (true) {_dataevent.waitone (); if (_currentqueue.count > 0) {_producerevent.reset (); _finishedevent.waitone (); Consumerqueue = _currentqueue; _currentqueue = (_currentqueue = = _writequeue)? _readqueue: _writequeue; _producerevent.set (); while (Consumerqueue.count > 0) {if (Consumerqueue.trydequeue (out user)) {FluentConsole.White.Background.Red.Line (user. ToString ()); allcount++; } FluentConsole.White.Background.Red.Line($ "Current number {Allcount. ToString ()}, which cost {watch. Elapsedmilliseconds.tostring ()}ms; "); System.Threading.Thread.Sleep (20); } } } } }
Fluentconsole is an output plugin for a console application, open source, interested in being able to play on your own.
output-side code
The first is performed using a double-buffered queue, and the second is performed using a lock lock. The following are the first method and CPU consumption when the second method executes.
We can see that with double queue buffering we reduce the CPU occupancy. But we may increase the time of execution.
Reference article: Http://www.codeproject.com/Articles/27703/Producer-Consumer-Using-Double-Queues
Someone else had thought about it in the 08, and I just had a little bit of an idea right now.
SOURCE download
Double buffering queue to reduce the competition for locks