学习了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;

}

多线程编程之Apue3rd_Chapter11之互斥锁_读写锁_自旋锁的更多相关文章

  1. JAVA锁机制-可重入锁,可中断锁,公平锁,读写锁,自旋锁,

    如果需要查看具体的synchronized和lock的实现原理,请参考:解决多线程安全问题-无非两个方法synchronized和lock 具体原理(百度) 在并发编程中,经常遇到多个线程访问同一个 ...

  2. 二、多线程基础-乐观锁_悲观锁_重入锁_读写锁_CAS无锁机制_自旋锁

    1.10乐观锁_悲观锁_重入锁_读写锁_CAS无锁机制_自旋锁1)乐观锁:就像它的名字一样,对于并发间操作产生的线程安全问题持乐观状态,乐观锁认为竞争不总是会发生,因此它不需要持有锁,将 比较-设置 ...

  3. 通俗易懂 悲观锁、乐观锁、可重入锁、自旋锁、偏向锁、轻量/重量级锁、读写锁、各种锁及其Java实现!

    网上关于Java中锁的话题可以说资料相当丰富,但相关内容总感觉是一大串术语的罗列,让人云里雾里,读完就忘.本文希望能为Java新人做一篇通俗易懂的整合,旨在消除对各种各样锁的术语的恐惧感,对每种锁的底 ...

  4. 写文章 通俗易懂 悲观锁、乐观锁、可重入锁、自旋锁、偏向锁、轻量/重量级锁、读写锁、各种锁及其Java实现!

    网上关于Java中锁的话题可以说资料相当丰富,但相关内容总感觉是一大串术语的罗列,让人云里雾里,读完就忘.本文希望能为Java新人做一篇通俗易懂的整合,旨在消除对各种各样锁的术语的恐惧感,对每种锁的底 ...

  5. Java锁的深度化--重入锁、读写锁、乐观锁、悲观锁

    Java锁 锁一般来说用作资源控制,限制资源访问,防止在并发环境下造成数据错误 锁作为并发共享数据,保证一致性的工具,在JAVA平台有多种实现(如 synchronized(重量级) 和 Reentr ...

  6. 【转载】同步和互斥的POSIX支持(互斥锁,条件变量,自旋锁)

    上篇文章也蛮好,线程同步之条件变量与互斥锁的结合: http://www.cnblogs.com/charlesblc/p/6143397.html   现在有这篇文章: http://blog.cs ...

  7. iOS多线程编程之NSThread的使用

      目录(?)[-] 简介 iOS有三种多线程编程的技术分别是 三种方式的有缺点介绍 NSThread的使用 NSThread 有两种直接创建方式 参数的意义 PS不显式创建线程的方法 下载图片的例子 ...

  8. iOS多线程编程之NSThread的使用(转)

    本文由http://blog.csdn.net/totogo2010/原创 1.简介: 1.1 iOS有三种多线程编程的技术,分别是: 1..NSThread 2.Cocoa NSOperation  ...

  9. [转]iOS多线程编程之NSThread的使用

    1.简介: 1.1 iOS有三种多线程编程的技术,分别是: 1..NSThread 2.Cocoa NSOperation (iOS多线程编程之NSOperation和NSOperationQueue ...

随机推荐

  1. Jquery的部分插件

    jQuery Easing:jQuery 动画效果扩展 jQuery Migrate:jQuery 应用迁移辅助插件Modernizr:专为HTML5和CSS3开发的功能检测类库jQuery Flex ...

  2. Linux xclock打不开时钟终端

    一般执行该操作的都是在安装oracle数据库或其他应用时,需要测试是否可以正常弹层执行的: 网络关于这个的描述和处理大片片的,但是符合自己实际情况的,还是需要直接去确认: 两步处理: 第一步: 使用r ...

  3. 如何处理错误信息 Pricing procedure could not be determined

    当给一个SAP CRM Quotation文档的行项目维护一个产品时,遇到如下错误信息:Pricing procedure could not be determined 通过调试得知错误消息在fun ...

  4. ABAP正则表达式 vs SPLIT INTO

    需求: 把如下通过"/"连接起来的三个字符串分别解析出来. 传统的做法见下图第98行的function module SKWF_UTIL_IO_FIND_BY_KEY: 这个fun ...

  5. out 和ref 的区别

    练习 1: 练习 2: 练习 3:

  6. EJB JPQL语句查询

    JPQL就是一种查询语言,具有与SQL 相类似的特征,JPQL是完全面向对象的,具备继承.多态和关联等特性,和hibernate HQL很相似.   查询语句的参数 JPQL语句支持两种方式的参数定义 ...

  7. 【题解】洛谷P1066 [NOIP2006TG] 2^k进制数(复杂高精+组合推导)

    洛谷P1066:https://www.luogu.org/problemnew/show/P1066 思路 挺难的一道题 也很复杂 满足题目要求的种数是两类组合数之和 r的最多位数m为 w/k(当w ...

  8. 旧文备份:硬盘MBR引导记录损坏的一种修复方法

    硬盘MBR信息损坏原因:硬盘上安装了windows XP和linux双系统,在windows下安装一套软件,破解的时候修改了硬盘的序列号,结果导致引导系统的grub无法完成linux的引导,只能进到w ...

  9. iOS 清除xcode缓存和生成文件

    方法1 按快捷键 shift+command+G 或者 Finder图标点击右键选 前往文件夹... 调出前往文件夹框 在里面输入如下 /Users/(自己电脑名字)/Library/Develope ...

  10. static函数

    C语言中使用静态函数的好处:      静态函数会被自动分配在一个一直使用的存储区,直到退出应用程序实例,避免了调用函数时压栈出栈,速度快很多.       关键字“static”,译成中文就是“静态 ...