学习了apue3rd的第11章,主要讲的是多线程编程。因为线程共享进程的资源比如堆和全局变量,多线程编程最重要的是,使用各种锁进行线程同步。

线程编程首先要学习的三个函数如下:

#include <pthread.h>

int pthread_create(pthread_t* tidp, const pthread_attr_t* restrict attr, void* (*start rm)(void*), void* restrict arg)

这个函数是负责线程创建的。第一个参数是线程id,线程创建成功后,线程id将被写入tidp指向的内存。Linux下,pthread_t是unsigned long 类型。第二个参数是一个结构体指针,是传给线程的属性,决定了线程的很多行为,如果不使用,可以传一个NULL给attr。第三个是线程的起始函数,其必须是void* xxxxx(void* arg)类型,就是返回值和参数都是void* 指针。第四个是传给启动函数的参数,启动之后,启动函数的参数arg的值等于pthread_create的arg的值。

void pthread_exit(void * rval_ptr)

这个函数可以将一个线程终止退出。rval_ptr是你设置的一个指针,它指向的内存可以保存你要返回的终止状态信息结构体。

int pthread_join(pthread_t thread, void** rval_ptr)

这个函数用于母线程回收其他线程的资源。rval_ptr指向的地址,将会写上从pthread_exit返回的指针的值,从而获取到终止状态结构体。

示例代码:   gcc main.c -o main -lpthread

#include <pthread.h>

#include <stdio.h>

#include <stdlib.h>

void* thread1(void* arg)

{

pthread_exit((void*)2);

}

int main(int argc, char* argv[])

{

pthread_t tid1;

int res;

void* rval;

res = pthread_create(&tid1,NULL,thread1,NULL);

if(res!=0)

{

printf("thread creating failed!\n");

exit(EXIT_FAILURE);

}

res = pthread_join(tid1,&rval);

if(res!=0)

{

printf("thread join failed\n");

exit(EXIT_FAILURE);

}

printf("thread exit code is %lu\n",(unsigned long)rval);//网上说无符号长整顿输出格式是//lu

return 0;

}

编译出现stray '\241' in program,出现原因是在word中打出的双引号,拷贝到文本文件中,与应该有的英文双引号不同,应该重新用英文输入法打双引号。EXIT_FAILURE的头文件为stdlib.h

互斥锁

互斥锁是类似posix信号量的线程同步手段,其本质是通过原子操作来获取一个锁。其函数如下。

int pthread_mutex_init(pthread_mutex_t* restrict mutex,const pthread_mutexattr_t* restrict attr)

这个函数是在使用互斥锁之前先初始化一下锁。其参数分别是互斥锁结构体和互斥锁属性结构体的指针。

int pthread_mutex_destroy(pthread_mutex_t* mutex)

这个函数是使用完之后,销毁互斥锁。

int pthread_mutex_lock(pthread_mutex_t* mutex)

int pthread_mutex_unlock(pthread_mutex_t* mutex)

这两个函数分别是获取锁和释放锁。

应该注意到是:如果一个线程已经获取到一个锁,并继续获取这个锁,就会产生死锁。

如果一个线程已经释放了一个锁,再次释放那个锁不会产生死锁,但在销毁锁的时候会出错。

互斥锁只存在于进程中,并不存在于内核中,因此,一个进程的锁如果没有销毁而就结束的话,内核不会有前面留下的锁。

int pthread_mutex_timedlock(pthread_mutex_t* restrict mutex,const struct timespec* restrict tsptr)

这个函数在获取锁的时候,可以避免发生死锁,如果到一个时间段获取不到锁,函数就会返回,返回码是EIMEDOUT。注意这里的tsptr是绝对时间,即从UTC1970-1-1 0:0:0开始计时的秒数和纳秒数。使用的时候需要使用clock_gettime获取系统绝对时间。然后再加上一个时间间隔,

gcc main.c -o main -lpthread -lrt   -lrt存在的原因是使用了clock_gettime,需要链接库rt。注意这里的”-”符号粘贴到Linux时候有时会出错,需要使用英文输入法重新打“-“。

使用时候一般把锁定义为全局变量,方便各个线程使用。测试代码如下(在上面代码基础上修改):

#include <pthread.h>

#include <stdio.h>

#include <stdlib.h>

#include <time.h>

pthread_mutex_t mutex1;

void* thread1(void* arg)

{

int res;

struct timespec tout;                       //定义在time.h

memset(&tout, 0, sizeof(tout));

clock_gettime(CLOCK_REALTIME, &tout);

tout.tv_sec += 10;

pthread_mutex_lock(&mutex1);

res = pthread_mutex_timedlock(&mutex1,&tout);

printf("timedlock return %d\n",res);

printf("%s\n",strerror(res));            //strerror在errno.h

//pthread_mutex_lock(&mutex1);

//pthread_mutex_unlock(&mutex1);

pthread_mutex_unlock(&mutex1);

pthread_exit((void*)2);

}

int main(int argc, char* argv[])

{

pthread_t tid1;

int res;

void* rval;

res = pthread_mutex_init(&mutex1,NULL);      //NULL定义在stdio.h

if(res!=0)

{

printf("mutex init failed!\n");

exit(EXIT_FAILURE);

}

res = pthread_create(&tid1,NULL,thread1,NULL);

if(res!=0)

{

printf("thread creating failed!\n");

exit(EXIT_FAILURE);

}

res = pthread_join(tid1,&rval);

if(res!=0)

{

printf("thread join failed\n");

exit(EXIT_FAILURE);

}

printf("thread exit code is %lu\n",(unsigned long)rval);//网上说无符号长整顿输出格式是//lu

res = pthread_mutex_destroy(&mutex1);

if(res!=0)

{

printf("mutex destroy failed!\n");

exit(EXIT_FAILURE);

}

return 0;

}

读写锁

读写锁的使用与互斥锁非常类似,它适合的场景是有大量读,而只有少量写的情况,可以让更多的线程往前执行。

int pthread_rwlock_init(pthread_rwlock_t* restrict rwlock,const pthread_rwlockattr_t* restrict attr)

int pthread_rwlock_destroy(pthread_rwlock_t* rwlock)

int pthread_rwlock_rdlock(pthread_rwlock_t* rwlock)

int pthread_rwlock_wrlock(pthread_rwlock_t* rwlock)

int pthread_rwlock_unlock(pthread_rwlock_t* rwlock)

自旋锁

自旋锁的时候与互斥锁类似,它适合的场景是线程每次执行的时间很短,其他线程不需要等多久的情况,自旋锁等待的时候,线程没有休眠,而是在不断的死循环。自旋锁适合的情况是自旋等待的开销比线程切换的开销小的情况。

int pthread_spin_init(pthread_spinlock_t* lock, int pshared)

int pthread_spin_destroy(pthread_spinlock_t* lock)

int pthread_spin_lock(pthread_spinlock_t* lock)

int pthread_spin_unlock(pthread_spinlock_t* lock )

int pthread_spin_trylock(pthread_spinlock_t* lock)

无论是互斥锁,或者是读写锁,或者自旋锁,或是以前学习的posix信号量,都存在一个问题,就是多个线程在竞争锁的时候,存在一个线程运行过快,从而反复获得锁,其他线程没有机会竞争到锁(复活线程需要时间)。需要在释放锁之后,空循环一定时间(5000-10000次),以便让其他线程充分时间获得锁。这是南京华为的面试官问我的问题。

gcc main.c -o main -lpthread –lrt

#include <pthread.h>

#include <stdio.h>

#include <stdlib.h>

#include <time.h>

pthread_mutex_t mutex1;

pthread_spinlock_t spin1;

pthread_rwlock_t rwlock1;

void* thread1(void* arg)

{

int res;

struct timespec tout;                       //定义在time.h

memset(&tout, 0, sizeof(tout));

clock_gettime(CLOCK_REALTIME, &tout);

tout.tv_sec += 5;

pthread_mutex_lock(&mutex1);

res = pthread_mutex_timedlock(&mutex1,&tout);

printf("timedlock return %d\n",res);

printf("%s\n",strerror(res));            //strerror在errno.h

//pthread_mutex_lock(&mutex1);

//pthread_mutex_unlock(&mutex1);

pthread_mutex_unlock(&mutex1);

pthread_spin_lock(&spin1);

pthread_spin_unlock(&spin1);

pthread_rwlock_rdlock(&rwlock1);

pthread_rwlock_unlock(&rwlock1);

pthread_rwlock_wrlock(&rwlock1);

pthread_rwlock_unlock(&rwlock1);

pthread_exit((void*)2);

}

int main(int argc, char* argv[])

{

pthread_t tid1;

int res;

void* rval;

res = pthread_mutex_init(&mutex1,NULL);      //NULL定义在stdio.h

if(res!=0)

{

printf("mutex init failed!\n");

exit(EXIT_FAILURE);

}

res = pthread_spin_init(&spin1,PTHREAD_PROCESS_PRIVATE);

if(res!=0)

{

printf("spin init failed!\n");

exit(EXIT_FAILURE);

}

res = pthread_rwlock_init(&rwlock1,NULL);

if(res!=0)

{

printf("rwlcok init failed!\n");

exit(EXIT_FAILURE);

}

res = pthread_create(&tid1,NULL,thread1,NULL);

if(res!=0)

{

printf("thread creating failed!\n");

exit(EXIT_FAILURE);

}

res = pthread_join(tid1,&rval);

if(res!=0)

{

printf("thread join failed\n");

exit(EXIT_FAILURE);

}

printf("thread exit code is %lu\n",(unsigned long)rval);//网上说无符号长整顿输出格式是//lu

res = pthread_mutex_destroy(&mutex1);

if(res!=0)

{

printf("mutex destroy failed!\n");

exit(EXIT_FAILURE);

}

res = pthread_spin_destroy(&spin1);

if(res!=0)

{

printf("spin destroy failed!\n");

exit(EXIT_FAILURE);

}

res = pthread_rwlock_destroy(&rwlock1);

if(res!=0)

{

printf("rwlock destroy failed!\n");

exit(EXIT_FAILURE);

}

return 0;

}

转载于:https://www.cnblogs.com/znwang/p/9235707.html

多线程编程之Apue3rd_Chapter11之互斥锁_读写锁_自旋锁相关推荐

  1. 华为应用锁退出立即锁_面试官:你说说互斥锁、自旋锁、读写锁、悲观锁、乐观锁的应用场景...

    前言 生活中用到的锁,用途都比较简单粗暴,上锁基本是为了防止外人进来.电动车被偷等等. 但生活中也不是没有 BUG 的,比如加锁的电动车在「广西 - 窃·格瓦拉」面前,锁就是形同虚设,只要他愿意,他就 ...

  2. 关抢占 自旋锁_互斥锁、自旋锁、读写锁、悲观锁、乐观锁的应用场景

    前言 生活中用到的锁,用途都比较简单粗暴,上锁基本是为了防止外人进来.电动车被偷等等. 但生活中也不是没有 BUG 的,比如加锁的电动车在「广西 - 窃·格瓦拉」面前,锁就是形同虚设,只要他愿意,他就 ...

  3. 面试官:你说说互斥锁、自旋锁、读写锁、悲观锁、乐观锁的应用场景?

    前言 生活中用到的锁,用途都比较简单粗暴,上锁基本是为了防止外人进来.电动车被偷等等. 但生活中也不是没有 BUG 的,比如加锁的电动车在「广西 - 窃·格瓦拉」面前,锁就是形同虚设,只要他愿意,他就 ...

  4. 分布式锁:互斥锁、自旋锁、读写锁、悲观锁、乐观锁

    前言 如何用好锁,也是程序员的基本素养之一了. 高并发的场景下,如果选对了合适的锁,则会大大提高系统的性能,否则性能会降低. 所以,知道各种锁的开销,以及应用场景是很有必要的. 接下来,就谈一谈常见的 ...

  5. 互斥锁、自旋锁、读写锁、悲观锁、乐观锁的应用场景

    前言 在编程世界里,「锁」更是五花八门,多种多样,每种锁的加锁开销以及应用场景也可能会不同. 如何用好锁,也是程序员的基本素养之一了. 高并发的场景下,如果选对了合适的锁,则会大大提高系统的性能,否则 ...

  6. 【剧前爆米花--爪哇岛寻宝】常见的锁策略——乐观锁、读写锁、重量级锁、自旋锁、公平锁、可重入锁等

    作者:困了电视剧 专栏:<JavaEE初阶> 文章分布:这是关于操作系统锁策略的文章,包括乐观锁.读写锁.重量级锁.自旋锁.公平锁.可重入锁等,希望对你有所帮助! 目录 乐观锁和悲观锁 悲 ...

  7. go语言基础-----18-----协程安全、互斥锁、读写锁、匿名锁、sync.Once

    1 线(协)程安全-互斥锁 竞态检查工具是基于运行时代码检查,而不是通过代码静态分析来完成的,可以添加-race 来执行竞态检测.但是对于那些没 有机会运行到的代码逻辑中如果存在安全隐患,即使加了-r ...

  8. Java中的锁机制 -- 乐观锁、悲观锁、自旋锁、可重入锁、读写锁、公平锁、非公平锁、共享锁、独占锁、重量级锁、轻量级锁、偏向锁、分段锁、互斥锁、同步锁、死锁、锁粗化、锁消除

    文章目录 1. Java中的锁机制 1.1 乐观锁 1.2 悲观锁 1.3 自旋锁 1.4 可重入锁(递归锁) 1.5 读写锁 1.6 公平锁 1.7 非公平锁 1.8 共享锁 1.9 独占锁 1.1 ...

  9. 嵌入式 自旋锁、互斥锁、读写锁、递归锁

    互斥锁(mutexlock): 最常使用于线程同步的锁:标记用来保证在任一时刻,只能有一个线程访问该对象,同一线程多次加锁操作会造成死锁:临界区和互斥量都可用来实现此锁,通常情况下锁操作失败会将该线程 ...

最新文章

  1. charts混合使用 elementui和e_vue模块化(echart+element ui)
  2. mysql高可用+keepalived
  3. map:erase删除元素之后迭代器失效的问题!(iterator not incrementable)
  4. UBUNTU804VirtualBox出现常见问题解决(转高手的)我转的CU的
  5. python作品讲解_python实例作品
  6. idea 2017 创建java_IDEA2017.3.3创建第一个javaweb项目及tomcat部署实战
  7. 设计灵感|资讯博客类App界面设计
  8. Ruby之散列与快排小程序
  9. 【python】python的二元表达式和三元表达式
  10. java里当显式请求注释时才接受类名称
  11. 神器!程序员必备的Linux命令行大全(PDF下载)
  12. ubuntu中网易云音乐图标打不开应用的问题
  13. js 根据公历日期 算出农历_利用Javascript获取当前日期的农历日期
  14. WSTMart商城系统数据字典
  15. 芯片设计中的时钟与约束
  16. iOS 自定义相机,拍照旋转
  17. 基于K210的MNIST手写数字识别
  18. Moloch安装与使用
  19. 机器学习——朴素贝叶斯分类
  20. 聚合类新闻客户端的改进

热门文章

  1. Android Q 不叫 Q,正式命名为 Android 10
  2. html5画安卓机器人,HTML5用户笔画形状检测机器人
  3. af_netlink_Linux Netlink通信机制详解(上)
  4. python单链表实现荷兰国旗问题_快速排序深入之荷兰国旗问题
  5. ionic4 QQ登陆集成
  6. java实验报告13答案_(完整版)Java程序设计实验报告
  7. Qt实现Linux下模拟点击界面,如何利用QT实现模拟鼠标点击?
  8. asp.net oracle连接数据库,ASP.NET连接Oracle数据库的步骤详解
  9. python读取日志错误信息_使用Python将Exception异常错误堆栈信息写入日志文件
  10. mysql 写入性能瓶颈_如何通过性能调优突破MySQL数据库性能瓶颈?