C++-多线程编程总结-实例讲解
C++ 多线程编程总结在开发C++程序时,一般在吞吐量、并发、实时性上有较高的要求。
设计C++程序时,总结起来可以从如下几点提高效率:•l 并发•l 异步•l 缓存下面将我平常工作中遇到一些问题例举一二,其设计思想无非以上三点。
1任务队列1.1 以生产者-消费者模型设计任务队列生产者-消费者模型是人们非常熟悉的模型,比如在某个服务器程序中,当User数据被逻辑模块修改后,就产生一个更新数据库的任务(produce),投递给IO模块任务队列,IO 模块从任务队列中取出任务执行sql操作(consume)。
设计通用的任务队列,示例代码如下:详细实现可参见:1.2 任务队列使用技巧1.2.1 IO 与逻辑分离比如网络游戏服务器程序中,网络模块收到消息包,投递给逻辑层后立即返回,继续接受下一个消息包。
逻辑线程在一个没有io操作的环境下运行,以保障实时性。
示例:注意,此模式下为单任务队列,每个任务队列单线程。
1.2.2 并行流水线上面的只是完成了io 和 cpu运算的并行,而cpu中逻辑操作是串行的。
在某些场合,cpu逻辑运算部分也可实现并行,如游戏中用户A种菜和B种菜两种操作是完全可以并行的,因为两个操作没有共享数据。
最简单的方式是A、B相关的操作被分配到不同的任务队列中。
示例如下:注意,此模式下为多任务队列,每个任务队列单线程。
1.2.3 连接池与异步回调比如逻辑Service模块需要数据库模块异步载入用户数据,并做后续处理计算。
而数据库模块拥有一个固定连接数的连接池,当执行SQL的任务到来时,选择一个空闲的连接,执行SQL,并把SQL 通过回调函数传递给逻辑层。
其步骤如下:•n 预先分配好线程池,每个线程创建一个连接到数据库的连接•n 为数据库模块创建一个任务队列,所有线程都是这个任务队列的消费者•n 逻辑层想数据库模块投递sql执行任务,同时传递一个回调函数来接受sql执行结果示例如下:注意,此模式下为单任务队列,每个任务队列多线程。
2. 日志本文主要讲C++多线程编程,日志系统不是为了提高程序效率,但是在程序调试、运行期排错上,日志是无可替代的工具,相信开发后台程序的朋友都会使用日志。
常见的日志使用方式有如下几种:•n 流式,如logstream << "start servie time[%d]" << time(0) << " app nam e[%s]" << app_string.c_str() << endl;•n Printf 格式如:logtrace(LOG_MODULE, "start servie time[%d] app name[% s]", time(0), app_string.c_str());二者各有优缺点,流式是线程安全的,printf格式格式化字符串会更直接,但缺点是线程不安全,如果把app_string.c_str() 换成app_string (std::string),编译被通过,但是运行期会crash(如果运气好每次都crash,运气不好偶尔会crash)。
我个人钟爱printf 风格,可以做如下改进:这样,除了标准类型+std::string 传入其他类型将编译不能通过。
这里只列举了一个参数的例子,可以重载该版本支持更多参数,如果你愿意,可以支持9个参数或更多。
•l 为日志增加颜色,在printf中加入控制字符,可以再屏幕终端上显示颜色,L inux下示例:printf("\033[32;49;1m [DONE] \033[39;49;0m")更多颜色方案参见:hi.baidu./jiemnij/blog/item/d95df8c28ac2815cb219a80e.html •l 每个线程启动时,都应该用日志打印该线程负责什么功能。
这样,程序跑起来的时候通过top –H – p pid 可以得知那个功能使用cpu的多少。
实际上,我的每行日志都会打印线程id,此线程id非pthread_id,而其实是线程对应的系统分配的进程id号。
3. 性能监控尽管已经有很多工具可以分析c++程序运行性能,但是其大部分还是运行在程序debug阶段。
我们需要一种手段在debug和release阶段都能监控程序,一方面得知程序瓶颈之所在,一方面尽早发现哪些组件在运行期出现了异常。
通常都是使用gettimeofday 来计算某个函数开销,可以精确到微妙。
可以利用C++的确定性析构,非常方便的实现获取函数开销的小工具,示例如下:Cost 应该被投递到性能统计管理器中,该管理器定时讲性能统计数据输出到文件中。
4 Lambda 编程使用foreach 代替迭代器很多编程语言已经建了foreach,但是c++还没有。
所以建议自己在需要遍历容器的地方编写foreach函数。
习惯函数式编程的人应该会非常钟情使用foreach,使用foreach的好处多多少少有些,如:.cnblogs./chsword/archive/2007/09/28/910011.html但主要是编程哲学上层面的。
示例:比如要实现dump 接口,不需要重写关于迭代器的代码实际上,上面的代码变通的生成了匿名函数,如果是c++ 11 标准的编译器,本可以写的更简洁一些:this->foreach([](user_t& user) {} );但是我大部分时间编写的程序都要运行在centos 上,你知道吗它的gcc版本是gcc 4.1.2,所以大部分时间我都是用变通的方式使用lambda函数。
Lambda 函数结合任务队列实现异步常见的使用任务队列实现异步的代码如下:这样做的缺点是,一个接口要响应的写两遍函数,如果一个函数的参数变了,那么另一个参数也要跟着改动。
并且代码也不是很美观。
使用lambda可以让异步看起来更直观,仿佛就是在接口函数中立刻完成一样。
示例代码:这样当要改动该接口时,直接在该接口修改代码,非常直观。
5. 奇技淫巧利用shared_ptr 实现map/reduceMap/reduce的语义是先将任务划分为多个任务,投递到多个worker中并发执行,其产生的结果经reduce汇总后生成最终的结果。
Shared_ptr的语义是什么呢?当最后一个shared_ptr析构时,将会调用托管对象的析构函数。
语义和map/reduce过程非常相近。
我们只需自己实现讲请求划分多个任务即可。
示例过程如下:•l 定义请求托管对象,加入我们需要在10个文件中搜索“oh nice”字符串出现的次数,定义托管结构体如下:C++多线程编程简单实例分类:Windows2012-04-17 16:43 3698人阅读评论(6) 收藏举报C++本身并没有提供任何多线程机制,但是在windows下,我们可以调用SDK win32 api来编写多线程的程序,下面就此简单的讲一下:创建线程的函数HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, // SDSIZE_T dwStackSize, // initial stack size LPTHREAD_START_ROUTINE lpStartAddress, // thread functionLPVOID lpParameter, // thread argument DWORD dwCreationFlags, // creation optionLPDWORD lpThreadId // thread identifier );在这里我们只用到了第三个和第四个参数,第三个参数传递了一个函数的地址,也是我们要指定的新的线程,第四个参数是传给新线程的参数指针。
eg1:#include <iostream>#include <windows.h>using namespace std;DWORD WINAPI Fun(LPVOID lpParamter){while(1) { cout<<"Fun display!"<<endl; }}int main(){HANDLE hThread = CreateThread(NULL, 0, Fun, NULL, 0, NULL);CloseHandle(hThread);while(1) { cout<<"main display!"<<endl; }return 0;我们可以看到主线程(main函数)和我们自己的线程(Fun函数)是随机地交替执行的,但是两个线程输出太快,使我们很难看清楚,我们可以使用函数VOID Sleep(DWORD dwMilliseconds // sleep time);来暂停线程的执行,dwMilliseconds表示千分之一秒,所以Sleep(1000);表示暂停1秒eg2:#include <iostream>#include <windows.h>using namespace std;DWORD WINAPI Fun(LPVOID lpParamter){while(1) { cout<<"Fun display!"<<endl; Sleep(1000);}}int main(){HANDLE hThread = CreateThread(NULL, 0, Fun, NULL, 0, NULL);CloseHandle(hThread);while(1) { cout<<"main display!"<<endl; Sleep(2000);}return 0;}执行上述代码,这次我们可以清楚地看到在屏幕上交错地输出Fun display!和main display!,我们发现这两个函数确实是并发运行的,细心的读者可能会发现我们的程序是每当Fun函数和main函数输出容后就会输出换行,但是我们看到的确是有的时候程序输出换行了,有的时候确没有输出换行,甚至有的时候是输出两个换行。
这是怎么回事?下面我们把程序改一下看看:eg3:#include <iostream>#include <windows.h>using namespace std;DWORD WINAPI Fun(LPVOID lpParamter){while(1) { cout<<"Fun display!\n"; Sleep(1000);}}int main(){HANDLE hThread = CreateThread(NULL, 0, Fun, NULL, 0, NULL);CloseHandle(hThread);while(1) { cout<<"main display!\n"; Sleep(2000);}return 0;}我们再次运行这个程序,我们发现这时候正如我们预期的,正确地输出了我们想要输出的容并且格式也是正确的。
c语言中什么是函数
c语言中什么是函数函数(function)表示每个输入值对应唯一输出值的一种对应关系。
这种关系使一个集合里的每一个元素对应到另一个(可能相同的)集合里的唯一元素。
函数f中对应输入值的输出值x的标准符号为f(x)。
包含某个函数所有的输入值的集合被称作这个函数的定义域,包含所有的输出值的集合被称作值域。
若先定义映射的概念,可以简单定义函数为,定义在非空数集之间的映射称为函数。
函数(Function)是可以重复使用的一段代码。
参数函数的一个明显特征就是使用时带括号( ),必要的话,括号中还要包含数据或变量,称为参数(Parameter)。
参数是函数需要处理的数据,例如:strlen(str1)用来计算字符串的长度,str1就是参数。
puts("C语言中文网")用来输出字符串,"C语言中文网"就是参数。
返回值既然函数可以处理数据,那么就有必要将处理结果告诉我们,所以很多函数都有返回值。
所谓返回值,就是函数的执行结果。
例如:char str1[] = "C Language";int len = strlen(str1);strlen 的执行结果是字符串str1 的长度,是一个整数,我们通过len 变量来接收。
函数返回值有固定的数据类型(int、char、float等),用来接收返回值的变量类型要一致。
库函数C语言开发者已经为我们编写好了几百个常用的函数,并分门别类的放到了不同的头文件(实际上仅仅是在头文件中进行了声明),使用函数时,要引入对应的头文件。
这些在C语言发布时已经编写好的函数叫做库函数;我们也可以编写自己的函数,叫做自定义函数。
自定义函数和库函数没有本质的区别,表现形式和使用方法一样,只是作者不同而已。
C语言中的sleep函数
C语⾔中的sleep函数
sleep函数:
功能:执⾏挂起⼀段时间
⽤法:unsigned sleep(unsigned seconds);
注意:
在VC中使⽤带上头⽂件#include<Windows.h>;
在Linux下,gcc编译器中,使⽤的头⽂件因gcc版本的不同⽽不同#include<unistd.h>
在VC中,Sleep中的第⼀个英⽂字“S”要⼤写,在linux下不要⼤写,在标准C中是sleep,不要⼤写,简单的说VC⽤Sleep,别的⽤sleep。
zaiVC中,Sleep()⾥的单位,试⾐毫秒为单位,所以如果想让函数滞留1秒的话,应该是Sleep(1000);在Linux下,sleep()⾥的单位是秒,⽽不是毫秒。
实例:
#include<windows.h>
int main()
{
int a;
a=1000;
Sleep(a);
return 0;
}
不记下⾯的
usleep函数:
功能:把进程挂起⼀段时间,单位是us(百万分之⼀秒)。
语法: void usleep(int micro-seconds);
返回值:⽆;
注意:这个函数不能⼯作在Windows操作系统中。
不记:
还有delay函数。
c语言go语句
c语言go语句
C语言早已成为了计算机编程领域中的经典之一,它作为一种高效、稳定、灵活的编程语言,被广泛应用于系统和应用软件的开发。
而Go语言又是近年来兴起的一种并发编程语言,一起来深入探讨一下C语言中的go语句吧!
一、什么是go语句
在C语言中,go语句的全称是gotos语句,它主要用于控制跳转。
当程序执行到go语句后,会跳转到语句标签所在位置继续执行,同时跳过中间的代码。
二、go语句的基础语法
语法格式如下:
```
goto 语句标签;
//代码段
语句标签:
```
此处的语句标签可以是任意标识符,而其所在行不能有其他语句,除了空行和注释。
三、go语句的应用场景
1. 在循环结构中使用,可以退出多重循环。
2. 在条件分支中使用,可以使代码更加的简洁。
3. 在软件开发中,可以跳过某些代码段,实现快速运行。
四、go语句设计的思考
尽管go语句有着很大的灵活性,在遇到多重循环和条件分支的情况下可以优雅的解决问题,但它也容易带来不可控的跳转,增强代码的混
乱性和维护不便,此时最好不要使用go语句。
五、总结
go语句虽然在C语言中存在一定的常见性问题,但它依然是一种很明智的编程方式。
在编程的时候,应根据具体情况确定是否合适使用go 语句,以充分利用其灵活性,保证程序的逻辑性和可读性。
c语言中arr的含义及用法
c语言中arr的含义及用法在C语言中,arr是数组的名称,它用来表示一系列相同类型的元素的集合。
数组可以存储在内存中连续的位置上,每个元素可以通过索引访问,索引从0开始。
arr的用法包括以下几个方面:1. 声明数组:在C语言中,我们可以使用arr来声明一个数组,指定数组的元素类型和数组的大小。
例如:int arr[5];声明了一个包含5个整数元素的数组。
2. 初始化数组:可以使用arr来对数组进行初始化,即为数组的每个元素指定一个初始值。
例如:int arr[5] = {1, 2, 3, 4, 5};初始化了一个包含5个整数元素的数组。
3. 访问数组元素:数组的每个元素可以通过索引来访问,使用arr[index]的形式,其中index表示所需元素的索引。
例如:int x = arr[2];将数组arr中的第3个元素赋值给变量x。
4. 修改数组元素:可以使用arr[index]的形式来修改数组的元素。
例如:arr[0] = 10;将数组arr的第1个元素的值修改为10。
5. 数组作为函数参数:在函数调用中,可以使用arr作为参数,将数组传递给函数。
函数可以通过形参接收并操作这个数组。
例如:void printArray(int arr[], int size);函数可以打印出传入的数组的所有元素。
6. 数组指针:arr也可以被解释为指向数组的指针。
当作为函数参数传递时,arr将退化为指针,失去了数组的大小信息。
可以通过指针运算来访问数组元素。
例如:int *ptr = arr;将arr赋值给指向整数的指针ptr,然后可以通过ptr来访问和修改数组的元素。
c语言赋值语句
c语言赋值语句C语言的赋值语句是指在C语言程序中,用来完成变量值的赋值过程的语句。
它表达了一个简单的逻辑:将一个值赋给一个变量,以便在以后可以引用这个变量。
语法格式如下:变量 =值;变量是一个存储数值的容器,可以是任何有效的C语言变量类型,如整型、浮点型、布尔型、字符型等,而数值则是一个C语言有效的常量,可以是整数、字符常量、浮点常量、字符串常量等。
赋值语句的作用是让变量的当前值和“=”右边的数值相等,给一个变量完成由另一个变量或一个表达式的赋值也是可以的,赋值语句中,右边的值先进行计算,然后再进行赋值操作。
例如,下面的语句,将初始值设置为4:int a = 4;在此,a是一个整型变量,4是一个整型常量,这条语句把4赋值给变量a,a的初始值就变成了4。
除了整型和浮点型数据,赋值语句还可以是字符型变量和字符串常量的赋值:char ch = c这个语句的意思是,将字符“c”赋值给ch变量,这里“c”既是字符常量也是字符串常量,而ch变量是字符型变量。
也可以把表达式的结果赋值给变量:int b = a + 4;这里,右边的表达式a+4先计算结果,得到8,然后把8赋值给变量b,最终b的值就变成了8。
还有一种特殊的赋值语句,称为复合赋值语句,用来表示将变量的值加上、减去某个数值,或将变量乘以、除以某个数值,与上面的赋值语句不同,复合赋值语句右边可以是一个表达式,也可以是一个常量,例如:b += 2;这条语句的意思是,把变量b的值加上2,即b = b + 2,结果b的值变为了10。
另外,也可以将另一个变量的值赋给另一个变量,例如:int c = a;这里,把变量a的值赋给变量c,最终,c的值就是4。
当然,在C语言程序中,赋值语句的使用比上面介绍的还要广泛的多,它既可以用在程序的开头定义变量,也可以在程序之中完成变量值的改变。
赋值语句不仅可以单独使用,它还可以与其它语句连用,此时它们组成一个更大的语句结构,包括while循环、for循环以及判断选择结构。
C语言程序设计-阿尔法编程(编程答案)
double r = 5.3;
/* 计算半径为 5.3 的圆的周长和面积 */
const double pi = 3.14159;
printf("周长: %f\n面积: %f\n", 2*pi*r, pi*r*r);
return 0;
注意:本题要求必须使用 const 常量定义PI,PI 为双精度小数,取 3.14159。
示例
输入
12
输出
周长: 75.398160
面积: 452.388960
#include <stdio.h>
int main () {
/* 使用 const 定义 pi */
const double PI=3.14159;
printf("%d",gavey);
return 0;
}
计算盐水浓度
#include <stdio.h>
int main() {
float nacl,water=95,jd;
scanf("%f",&nacl);
jd=nacl/(nacl+water);
}
printf("\n");
}
return 0;
}
求粮食分配
#include <stdio.h>
int main() {
int putin,total,jo=2,yo=1;
float jafter=0,gavej=0;
注意:PI 取 3.14159;输入的半径 r 的数据类型是双精度浮点数。
c语言中%是什么意思及用法
c语言中%是什么意思及用法
在C语言中,%的用法主要有两种:
1.若%前后均为整形,此时%表示求余运算符,经常被用来判断一个整型数是不是能被另一个整型数整除。
如9%5,表达含义为9对5取余数,结果为4。
2若为%+格式字符。
如%d或者%f等等,表示将输入的数据转为为指定的格式来进行输出。
常用的转化格式为:%c 输入单个字符,所以在输入多个字符时,它只读取第一个字符。
%s 字符串输出,能够读取一个字符串。
%d 整型输出,%ld长整型输出,%o以八进制数形式输出,%x以十六进制形式输出, %f以小数形式输出。
常用形式如下:
printf(“%s”,str);此时,程序将以字符串格式输出变量str。
printf(“%c”,ch);此时,程序将以单个字符格式输出变量ch。
完整代码如下:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
int main()
{
int num=5;
char c=’a’;
printf("%d ",num);
printf("%c ",c);
system("pause");
return 0;
}。
C语言--strcmp()函数
C语⾔--strcmp()函数strcmp函数是string compare(字符串⽐较)的缩写,⽤于⽐较两个字符串并根据⽐较结果返回整数。
基本形式为strcmp(str1,str2),若str1=str2,则返回零;若str1<str2,则返回负数;若str1>str2,则返回正数。
⾸先说明strcmp的使⽤格式:strcmp(char1 *, char2 *);该函数功能是:从第⼀个元素开始,依次⽐较字符串数字char1和char2,如果char1的第n个元素⽐char2d的第n个元素⼤,则返回值为⼤于0 的数,相反的话,则返回⼩于0的数,如果相等,则继续⽐较 n+1个元素,直到第n+1个不相等,就停⽌⽐较,并返回相应的值。
(注意:char1 *这⾥实际放的是⼀个指针,字符数组名实际就是⼀个常量指针)举例:char name_1[10]={"zhangsan"}; name_2[10]={"zhaoliu"};syrcmp(name_1,name_2);⽐较的结果是:因为“z”“h”“a”都相等,所以继续⽐较第四个元素“n” 和“o”;⽐较⾄此,已经有返回值了,所以停⽌后边的⽐较,并返回相应的值。
应⽤实例;按照字母从⼩到⼤输出姓名char name[5][20]={"zhangsan","lisi","wangwu","zhaoliu","any"};//k控制⽐较的趟数for(int k=0; k<5;k++){//i控制⽐较的第⼀维空间for (int i = 0; i<4; i++) {//j控制每个姓名的第j个字母for (int j = 0; j<20; j++) {//利⽤strcmp 函数⽐较字母⼤⼩if (strcmp(&name[i][j], &name[i+1][j])>0) { //使⽤&的原因是:字符数组名的本质是⼀个指针,指向⼀个地址。
c语言的占行语句
c语言的占行语句
C语言的占行语句主要是指对程序中某一行或几行代码进行注释或占位,以便于代码的阅读和维护。
在C语言中,常见的占行语句包括注释符号、空行、宏定义等。
注释符号是最常见的占行语句,用来对代码进行注释。
在C语言中,注释符号可以使用两种形式:单行注释和多行注释。
单行注释使用“//”作为注释符号,多行注释使用“/* */”作为注释符号。
空行是指在代码中添加一行或多行空白行,以便于代码的阅读和布局。
在C语言中,空行并不会影响程序的执行,但可以提高代码的可读性。
宏定义是指在代码中定义一个宏,用来代替某一段代码。
宏定义可以使用“#define”关键字进行定义。
例如,我们可以定义一个宏“#define PI 3.1415926”,在代码中使用“PI”代替3.1415926。
总之,C语言的占行语句对于代码的阅读和维护非常重要,程序员应该合理地使用这些占行语句,以便于代码的管理和维护。
- 1 -。
C语言习题及解答
C 语言习题及解答1、输入一个华氏温度,要求输出摄氏温度。
公式为#include <stdio 。
h> void main ( ) {float C ,F;printf(”Input F :"); scanf ("%f”,&F ); C=5.0/9*(F-32); printf ("C=%.2f\n”,C);}2、编写程序,从键盘输入一个大写字母,将它转换为对应的小写字母后输出。
(提示:同一个字母的大写比小写小32)#include 〈stdio 。
h 〉 void main ( ) { char ch ;printf("Input ch:”); scanf("%c”,&ch); ch=ch+32;printf (“ch=%c\n",ch);}3、编写程序,输入梯形的上底、下底和高,计算并输出梯形的面积。
#include <stdio 。
h 〉void main ( ) { float a ,b,h ,area;printf (”Input a ,b ,h : ”); scanf(”%f%f%f", &a ,&b ,&h ); area=(a+b )*h/2;printf (”area=%.2f\n", area );}4、编写程序,输入圆半径r ,求圆周长、圆面积、圆球表面积、圆球体积. #include 〈stdio.h>#define PI 3.1415926 void main( ){float r,L ,s1,s2,V;printf (”Input r :"); scanf(”%f”, &r ); L=2*PI *r ; s1=PI*r*r ; s2=4*PI *r *r; V=4。
0/3*PI *r *r *r ;printf (”L=%.2f , s1=%.2f, s2=%。
