In the development of C + + programs, generally in the throughput, concurrency, real-time high requirements. When designing a C + + program, the following points can be summed up to improve efficiency:
Concurrent
Asynchronous
Cache
The following will be my usual work encountered some problems twos, the design of the idea is nothing more than three points.
1 Task Queue
1.1 Designing task queues with producer-consumer models
The producer-consumer model is a very familiar model, such as in a server program, when the user data is modified by the logical module, it generates a task of updating the database (produce), to the IO Module task queue, The IO module pulls the task execution SQL operation (consume) from the task queue.
To design a common task queue, the sample code is as follows:
Detailed implementations can be found in:
Http://ffown.googlecode.com/svn/trunk/fflib/include/detail/task_queue_impl.h
void task_queue_t::p roduce (const task_t& task_) {lock_guard_t lock (M_mutex), if (M_tasklist->empty ()) {//! The condition satisfies the wake-up Wait thread m_cond.signal ();} M_tasklist->push_back (task_);} int Task_queue_t::comsume (task_t& task_) {lock_guard_t lock (M_mutex); while (M_tasklist->empty ())//! when there is no job, Wait until the condition is met wake {if (false = = M_flag) {return-1;} M_cond.wait ();} Task_ = M_tasklist->front (); M_tasklist->pop_front (); return 0;}
1.2 Task Queue Usage tips
1.2.1 IO and logic separation
For example, in a network game server program, the network module receives a message packet, which is returned immediately after it is posted to the logical layer, continuing to accept the next message packet. Logical threads operate in an environment that does not have an IO operation to guarantee real-time performance. Example:
void Handle_xx_msg (long uid, const xx_msg_t& msg) {logic_task_queue->post (Boost::bind (&servie_t::p roces, UID, msg));}
Note that this mode is a single-task queue, with each task queue single-threaded.
1.2.2 Parallel pipelining
The above only completes the parallel of IO and CPU operation, while the logical operation in CPU is serial. In some cases, the CPU logic operation part can also be implemented in parallel, such as in-game User a vegetables and b vegetables two operations can be completely parallel, because two operations do not share data. The simplest way is that A and B related operations are assigned to different task queues. Examples are as follows:
void Handle_xx_msg (long uid, const xx_msg_t& msg) {logic_task_queue_array[uid% sizeof (Logic_task_queue_array)]- >post (Boost::bind (&servie_t::p roces, UID, msg));}
Note that this mode is a multi-task queue, with one single thread per task queue.
1.2.3 Connection pooling and asynchronous callbacks
For example, the logic service module needs the database module to load the user data asynchronously, and do the subsequent processing calculation. While the database module has a connection pool with a fixed number of connections, when the task of executing SQL arrives, select an idle connection, execute the SQL, and pass SQL through the callback function to the logical layer. The steps are as follows:
Pre-allocating thread pools, each creating a connection to the database
Create a task queue for the database module, and all threads are consumers of this task queue
The logical layer wants the database module to post SQL to perform tasks while passing a callback function to accept the results of SQL execution
Examples are as follows:
void Db_t:load (Long uid_, Boost::functionpost (Boost::bind (&db_t:load, UID, func));
Note that this mode is a single-task queue, with each task queue multithreaded.
2. Log
This article mainly talk about C + + multithreaded programming, the log system is not to improve the efficiency of the program, but in the program debugging, run the wrong time, the log is an irreplaceable tool, I believe that the development of background program friends will use the log. There are several common ways to use logs:
Streaming, such as LogStream << "start Servie time[%d]" << time (0) << app name[%s] << app_string.c_str () <& Lt Endl
Printf format such as: Logtrace (Log_module, "Start Servie time[%d] app name[%s]", Time (0), app_string.c_str ());
Both have advantages and disadvantages, streaming is thread-safe, printf format format string will be more direct, but the disadvantage is that thread is unsafe, if the APP_STRING.C_STR () replaced by app_string (std::string), the compilation is passed, But the run time will be crash (if the luck is good every time crash, bad luck occasionally will crash). I personally love the printf style and can do the following improvements:
Thread safety can be achieved by increasing thread safety and using the traits mechanism of C + + templates. Example:
Templatevoid logtrace (const char* module, const char* FMT, ARG1 arg1) {Boost::format s (FMT); F% arg1;}
This way, except for standard type +std::string incoming other types will compile without passing. Here is an example of a parameter that can be overloaded to support more parameters, and can support 9 parameters or more if you wish.
Add color to the log, add control characters to printf, and then display the color on the screen terminal, linux example: printf ("33[32;49;1m [done] 33[39;49;0m")
For more color schemes see:
Http://hi.baidu.com/jiemnij/blog/item/d95df8c28ac2815cb219a80e.html
When each thread starts, it should use the log to print what function the thread is responsible for. This way, when the program runs up through the top–h–p PID can tell how much the function uses the CPU. In fact, every line of my log prints the thread ID, which is not pthread_id, but is actually the process ID number of the system assigned by the thread.
3. Performance monitoring
Although there are many tools that can analyze the performance of C + + programs, most of them run in the program debug phase. We need a means to monitor the program in both the debug and release phases, on the one hand, the bottleneck of the program, and the early detection of which components in the run-time anomalies.
It is common to use gettimeofday to calculate a function cost that can be precise to subtle. Can take advantage of the deterministic destructor of C + +, a very convenient implementation of the function to get the cost of the gadget, examples are as follows:
struct Profiler{profiler (const char* func_name) {gettimeofday (&TV, NULL);} ~profiler () {struct Timeval tv2;gettimeofday (&TV2, NULL); Long cost = (tv.tv_sec-tv.tv_sec) * 1000000 + (TV.TV_USEC- TV.TV_USEC);//! Post to some manager}struct timeval TV;}; #define Profiler () Profiler (__function__)
Cost should be posted to the performance statistics manager, which regularly outputs the performance statistics to a file.
4 LAMBDA Programming
Using foreach instead of iterators
Many programming languages have built-in foreach, but C + + has not. It is recommended that you write the foreach function where you need to traverse the container. People who are accustomed to functional programming should be very fond of using foreach, and some of the benefits of using foreach are some, such as:
Http://www.cnblogs.com/chsword/archive/2007/09/28/910011.html
But mainly in the aspect of programming philosophy.
Example:
void User_mgr_t::foreach (Boost::function func_) {for (Iterator it = M_users.begin (); It! = M_users.end () ++it) {func_ (it-& Gt;second);}}
For example, to implement the dump interface, you do not need to rewrite the code about the iterator
void User_mgr_t:dump () {struct lambda {static void print (user_t& user) {//! print (ToString (user);}}; This->foreach (lambda::p rint);}
In fact, the above code is a workaround to generate anonymous functions, if it is the C + + 11 standard compiler, can write more concise:
This->foreach ([] (user_t& user) {});
But most of the time I've written a program that runs on CentOS, you know, its GCC version is GCC 4.1.2, so most of the time I use lambda functions in a flexible way.
LAMBDA functions with task queues for asynchronous
The common code to implement async using a task queue is as follows:
void Service_t:async_update_user (Long uid) {task_queue->post (Boost::bind (&service_t:sync_update_user_impl, This, UID));} void Service_t:sync_update_user_impl (long uid) {user_t& user = Get_user (UID); User.update ()}
The disadvantage of this is that an interface to respond to the write two-pass function, if the parameters of a function has changed, then another parameter should be changed. And the code is not very beautiful either. Using lambda can make async look more intuitive, as if it were done immediately in an interface function. Example code:
void Service_t:async_update_user (long uid) {struct lambda {static void Update_user_impl (service_t* servie, long uid) { user_t& user = Servie->get_user (UID); User.update ();}}; Task_queue->post (Boost::bind (&lambda:update_user_impl, this, UID));}
This makes it very straightforward to modify the code directly within the interface when you want to change the interface.
5. Artifice
Implement Map/reduce with shared_ptr
The semantics of Map/reduce is that the task is divided into multiple tasks, delivered to multiple workers and executed concurrently, resulting in a result that is aggregated by reduce to produce the final result. What is the semantics of shared_ptr? When the last shared_ptr is destructor, the destructor for the managed object is called. The semantics and map/reduce processes are very similar. We just need to make our own request to divide multiple tasks. The sample procedure is as follows:
Define the request managed object, add the number of times we need to search for "oh nice" strings in 10 files, define the managed structure as follows:
struct reducer{void set_result (int index, long result) {M_result[index] = result;} ~reducer () {Long total = 0;for (int i = 0; i < sizeof (M_result), ++i) {total + = M_result[i];} //! Post total to Somewhere}long m_result[10];};
Define the worker that performs the task
void Worker_t:exe (int index_, shared_ptr ret) {ret->set_result (index, 100);}
After splitting a task, post it to a different worker
SHARED_PTR ret (new reducer ()); for (int i = 0; i <; ++i) {Task_queue[i]->post (Boost::bind (&worker_t:exe, I, R ET));}
The above is the C + + multithreaded programming summary of content, more relevant content please pay attention to topic.alibabacloud.com (www.php.cn)!