8种机械键盘轴体对比
本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选?
POSIX
POSIX是一种标准,有多线程编程标准、网络编程标准等。
POSIX多线程
Linux下,一般多线程的实现由POSIX多线程编程实现。Android系统属于Linux系统,因此NDK原生支持POSIX多线程编程。
Windows平台一般用Windows自带的API。
POSIX的编译:
在Linux平台中采用gcc编译(先编译生成目标文件然后链接生成可执行程序):gcc test.c -o test -lpthread,执行:./test。
POSIX帮助文档的查看:在Linux系统中,安装POSIX帮助文档:sudo apt-get install manpages-posix-dev
列出所有函数man -k pthread;查看某个函数:man pthread_create
创建线程与结束线程
01.c:#include
#include
#include
//必须引入的头文件
#include
//一个相当于Java的run方法
void* thr_fun(void* arg){
//得到线程创建的参数
char* no = (char*)arg;
int i = 0;
for(; i < 10; i++){
printf("%s thread, i:%dn",no,i);
if(i==5){
//线程退出(自杀)
pthread_exit(2);
//他杀pthread_cancel
}
}
//run方法执行完,线程结束,返回
return 1;
}
void main(){
printf("main threadn");
//线程id
pthread_t tid;
//线程的属性,NULL默认属性
//thr_fun,线程创建之后执行的函数,可以传入参数,在thr_fun方法的arg中可以取出
pthread_create(&tid,NULL,thr_fun,"1");
void* rval;
//等待tid线程结束
//thr_fun在线程退出时传入的参数,都作为第二个参数的内容
pthread_join(tid,&rval);
printf("rval:%dn",(int)rval);
}
书写完成编译gcc 01.c -o 01 -lpthread,执行./01,输出结果如下:main thread
1 thread,i:0
1 thread,i:1
1 thread,i:2
1 thread,i:3
1 thread,i:4
1 thread,i:5
rval:2
在代码中:
通过pthread_create创建线程,需要传入一个函数指针,相当于Java线程中的run方法。然后还需要传参,参数可以在run方法中取出。
线程被创建以后,就会执行“run”方法,该方法中可以拿到线程创建的参数,可以自杀掉线程。线程的结束需要参数。
可以通过pthread_join方法等待线程结束,并且可获取线程结束的参数。
线程加锁
在多线程中我们需要一个线程执行完再执行另一个线程就需要给线程加锁。
02.c:#include
#include
#include
#include
int i = 0;
//互斥锁
pthread_mutex_t mutex;
void* thr_fun(void* arg){
//加锁
pthread_mutex_lock(&mutex);
char* no = (char*)arg;
for(;i < 5; i++){
printf("%s thread, i:%dn",no,i);
sleep(1);
}
i=0;
//解锁
pthread_mutex_unlock(&mutex);
}
void main(){
pthread_t tid1, tid2;
//初始化互斥锁
pthread_mutex_init(&mutex,NULL);
pthread_create(&tid1,NULL,thr_fun,"No1");
pthread_create(&tid2,NULL,thr_fun,"No2");
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
//销毁互斥锁
pthread_mutex_destroy(&mutex);
}
书写完成编译gcc 02.c -o 02 -lpthread,执行./02,输出结果如下:No2 thread, i:0
No2 thread, i:1
No2 thread, i:2
No2 thread, i:3
No2 thread, i:4
No1 thread, i:0
No1 thread, i:1
No1 thread, i:2
No1 thread, i:3
No1 thread, i:4
在代码中:
我们通过pthread_mutex_init初始化了一把互斥锁,最后通过pthread_mutex_destroy进行销毁。
在线程执行的时候,我们可以通过pthread_mutex_lock、pthread_mutex_unlock进行加锁和解锁。
使用互斥锁可以解决线程死锁(ABBA)的问题。
互斥锁是先让一个线程做完,然后另外一个线程做。还有一种情况就是,一个线程先执行生产,然后另外一个线程就会去消费。
其实视频解码的绘制使用的就是生产者–消费者的模式。图片的下载显示也是基于这种模式。比如说我们生产者生成的产品,放到一个队列里面,当生产者生产出产品的时候就会发送信号通知消费者去消费,例如RTMP推流的时候,我们本地采集音视频的时候就需要一种队列,因为本地的压缩比网络上传要快。
使用这一种模式,就需要条件变量。举例:#include
#include
#include
#include
//模拟产品队列
int productNum = 0;
//互斥锁
pthread_mutex_t m;
//条件变量
pthread_cond_t c;
void *produce(void* arg){
char* no = (char*)arg;
for (;;){
//加锁
pthread_mutex_lock(&m);
//生产者生产产品
productNum++;
printf("%s生产产品:%dn", no, productNum);
//通知消费者进行消费
pthread_cond_signal(&c);
//解锁
pthread_mutex_unlock(&m);
sleep(1);
}
return (void*)1;
}
void *comsume(void* arg){
char* no = (char*)arg;
for (;;){
pthread_mutex_lock(&m);
//使用while是为了防止惊群效应唤醒条件变量
while (productNum == 0){
//1.没有产品可以消费,等待生产者生产,即等待条件变量被唤醒
//2.释放互斥锁,使得其他消费者可以进来等待
//3.被唤醒的时候,解除阻塞,重新申请获得互斥锁,保证只有一个消费者消费
pthread_cond_wait(&c, &m);
}
productNum--;
printf("%s消费者消费产品:%dn", no, productNum);
pthread_mutex_unlock(&m);
sleep(1);
}
return (void*)1;
}
void main(){
printf("main threadn");
//初始化互斥锁
pthread_mutex_init(&m, NULL);
//初始化条件变量
pthread_cond_init(&c, NULL);
pthread_t thread_producer;
pthread_t thread_comsumer;
//创建线程,指定run方法,并且可以传入参数,在run方法的arg中可以取出
pthread_create(&thread_producer, NULL, produce, "producer");
pthread_create(&thread_comsumer, NULL, comsume, "comsumer");
//等待线程结束,获取线程返回参数
pthread_join(thread_producer, NULL);
pthread_join(thread_comsumer, NULL);
//销毁互斥锁
pthread_mutex_destroy(&m);
//销毁条件变量
pthread_cond_destroy(&c);
}
书写完成编译gcc 03.c -o 03 -lpthread,执行./03,输出结果如下:main thread
producer生产产品:1
comsumer消费者消费产品:0
producer生产产品:1
comsumer消费者消费产品:0
producer生产产品:1
comsumer消费者消费产品:0
producer生产产品:1
comsumer消费者消费产品:0
producer生产产品:1
comsumer消费者消费产品:0
producer生产产品:1
comsumer消费者消费产品:0
producer生产产品:1
comsumer消费者消费产品:0
这里我通过sleep的方式控制了生产者与消费者的效率,一般来说生产的速度要比消费的速度快。
上面是只有一个生产者和一个消费者的示例代码。一般来说,生产者和消费者都会有多个。这里我们通过线程数组的方式来实现。
举例如下:#include
#include
#include
#include
//消费者数量
#define CONSUMER_NUM 2
//生产者数量
#define PRODUCER_NUM 1
pthread_t pids[CONSUMER_NUM+PRODUCER_NUM];
//产品队列
int ready = 0;
//互斥锁
pthread_mutex_t mutex;
//条件变量
pthread_cond_t has_product;
//生产
void* producer(void* arg){
int no = (int)arg;
//条件变量
for(;;){
pthread_mutex_lock(&mutex);
//往队列中添加产品
ready++;
printf("producer %d, produce productn",no);
//fflush(NULL);
//通知消费者,有新的产品可以消费了
//会阻塞输出
pthread_cond_signal(&has_product);
printf("producer %d, singaln",no);
pthread_mutex_unlock(&mutex);
sleep(1);
}
}
//消费者
void* consumer(void* arg){
int num = (int)arg;
for(;;){
pthread_mutex_lock(&mutex);
//while?
//superious wake ‘惊群效应’
while(ready==0){
//没有产品,继续等待
//1.阻塞等待has_product被唤醒
//2.释放互斥锁,pthread_mutex_unlock
//3.被唤醒时,解除阻塞,重新申请获得互斥锁pthread_mutex_lock
printf("%d consumer waitn",num);
pthread_cond_wait(&has_product,&mutex);
}
//有产品,消费产品
ready--;
printf("%d consume productn",num);
pthread_mutex_unlock(&mutex);
sleep(1);
}
}
void main(){
//初始化互斥锁和条件变量
pthread_mutex_init(&mutex,NULL);
pthread_cond_init(&has_product,NULL);
printf("initn");
int i;
for(i=0; i
//生产者线程
printf("%dn",i);
pthread_create(&pids[i],NULL,producer,(void*)i);
}
for(i=0; i
//消费者线程
pthread_create(&pids[PRODUCER_NUM+i],NULL,consumer,(void*)i);
}
//等待
sleep(10);
for(i=0; i
pthread_join(pids[i],NULL);
}
//销毁互斥锁和条件变量
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&has_product);
}
gdb调试
在Linux中通过gdb进行调试,步骤如下:编译加上-g参数
gcc test1.c -g -o test1
进入调试
gdb test1
开始调试
start
显示代码
list-简写l
list 函数名称(查看函数内容)
list 行数(查看某行代码)
执行下一步
next-简写n
查看变量
print 变量名-简写p
进入到某个函数
step-简写s
设置断点
break 行号(gdb中的行号)-简写b
全速运行
continue-遇到断点会停止-简写b
查看断点信息
info breakpoints
删除断点
delete breakpoints 断点编号
修改变量的值
set var 变量=值
程序调用堆栈
当前函数之前的所有已调用函数列表,每一个都分配一个“帧”,最近调用的函数在0号帧里
backtrace-简写bt
切换栈帧
frame 1(查看指定栈帧的变量)
自动显示
display 变量名
取消自动显示
undisplay 行号(自动显示的行号)
查看内存布局
x /20 地址
x /20 buff-查看buff数组的前20个元素
在linux中运行程序,程序非正常退出,如何查看错误?ulimit -a 查看core文件是否分配大小
ulimit -c 1024 创建的core文件大小为1024字节
gcc test2.c -g -o test2 编译链接得到带有-g选项的可执行程序
./test2 执行程序,会生成core日志文件
gdb test2 core 打开日志文件,定位错误信息到具体的代码行数