线程高级操作(二)

之前在线程高级操作中说到了线程的高级操作包括修改线程的属性和进行线程之间的同步操作。线程的同步有两种方式,一种是使用互斥量一种是使用读写锁。上一篇文章说的是互斥量,这篇文章主要介绍的是读写锁。

读写锁与互斥量类似,但是读写锁相对于互斥量来说最大的特点就是并行性高。互斥锁每次只有一个线程可以得到锁进行操作,其他的线程处于阻塞状态。多线程的意义就在于高并发性,但是使用互斥量就会将并行操作编程串行操作,程序的效率会大打折扣。

读写锁的优点是读写锁分为两种,读线程和写线程。读线程只对共享资源进行读操作,不修改资源,写操作对共享资源进行写操作,改变共享资源。在执行程序是,如果对于读操作的线程远远大于写线程的时候,使用读写锁可以提高线程的并发。

1.初始化读写锁

在Linux环境下使用pthread_rwlock_t表示读写锁,使用pthread_rwlock_init()函数对读写锁进行初始化。

 int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restic attr) 函数:

头文件: #include <pthread.h> 

参数:第一个参数rwlock是读写锁的指针,读写锁在该函数内被初始化,通过该参数返回给调用者。第二个参数是读写锁的属性,一般设置为NULL,系统默认对其进行初始化。

返回值:成功返回0,失败返回错误号。

函数功能:初始化一个读写锁,初始成功之后,第一个参数rwlock就是一个读写锁指针,可供其他进程使用。

当一个读写锁不在使用的时候,需要销毁读写锁,Linux环境下使用 pthread_rwlock_destroy() 函数销毁一个读写锁。

 pthread_rwlock_destroy(pthread_rwlock_t *rwlock) 函数:

头文件: #include <pthread.h> 

参数:参数rwlock是要销毁的读写锁的指针。

返回值:成功返回0,失败返回错误号。

函数功能:销毁不用的读写锁。

2.得到与释放读写锁

得到读写锁:

 pthread_rwlock_rdlock(pthread_rwlock_t * rwlock) 函数:函数参数是一个读写锁,如果该读写锁已经被某一线程在读模式下使用,该线程还可以用到该锁。如果该读写锁已经被某一线程在写模式下使用,此时会出现线程阻塞直到读写锁被释放。

 pthread_rwlock_tryrdlock(pthread_rwlock_t * rwlock) 函数: 该函数的返回值和参数与pthread_rwlock_rdlock一样,唯一不同的是pthread_rwlock_tryrdlock()函数得不到读写锁的时候,会返回一个错误编号BUSSY,不会导致阻塞。

同样的,可以理解 pthread_rwlock_wrlock(pthread_rwlock_t * rwlock) 函数和 pthread_rwlock_trywrlock(pthread_rwlock_t * rwlock) 函数的用法。

释放读写锁:

pthread_rwlock_unlock() 函数释放读写锁。

3.使用读写锁实例

之前的线程高级操作(一)中我们用互斥量操作一个链表,这次用读写锁来操作一个列表,而且可以通过两个程序的对比来比较读写锁与互斥量的效率的对比。

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <pthread.h>

#include <sys/time.h>

#include <time.h>

typedef struct job * Job;

/* 链表结点结构 */

struct job{

       pthread_t tid;       /* 线程ID */

       Job next;       /* 下一个链表结点 */

       int val;           /* 结点值 */

};

struct timeval begintime;

int insert(Job head, int val, pthread_t tid)

{

       Job p, q;

       p = head;             /* 头指针 */

       if(p != NULL){ /* 判断空链表的情况 */

              while(p->next != NULL){

                     p = p->next;

              }

       }

       q = (Job)malloc(sizeof(struct job)); /* 为结点分配内存空间 */

       if(q == NULL){

              perror("fail to malloc");

              return -1;

       }

       q->next = NULL;

       q->val = val;

       q->tid = tid;  /* 设置结点的所有者,线程1 */

       if(p == NULL){      /* 设置链表头指针 */

              head = q;

              return 0;

       }

       p->next = q;        /* 插入到队列中 */

       return 0;

}

int free_job(Job head)

{

       Job p,q;

       for(p = head; p != NULL; p = p->next){ /* 线程退出时释放所有的任务结点 */

              q = p;

              free(q);

       }

       return 0;

}

void print(Job head)

{

       Job p;

       for(p = head->next; p != NULL; p = p->next) /* 输出取得的任务列表 */

              printf("thread %u: %d\n", (unsigned int)p->tid, p->val);

}

void * tf10(void * arg)

{

       pthread_rwlock_t q_rwlock;

       long long count; /* count of jobs */

       Job p,q;

       Job task = NULL;

       task = (struct job *)malloc(sizeof(struct job)); /* 设置头结点,该结点不 存储有效信息 */

       task->next = NULL;

       task->val = 0;

       task->tid = -1;

       pthread_t tid;

       struct timeval endtime;

       float elapsed_time;

       tid = pthread_self(); /* 得到线程ID */

       count = 0;

       while(count < 100000000){

              if(pthread_rwlock_tryrdlock(&q_rwlock) == 0){ /* 锁住队列,使用的是读锁 */

                     q = arg;

                     p = q->next;

                     while(p != NULL){ /* 遍历队列,寻找属于该线程的作业结构 */

                            if(tid == p->tid){

                                   count++;

                            }

                                   p = p->next;

                     }

                     pthread_rwlock_unlock(&q_rwlock); /* 释放读锁 */

              }

       }

       gettimeofday(&endtime, NULL); /* 得到线程结束运行的时间 */

       elapsed_time = 1000000 * (endtime.tv_sec-begintime.tv_sec) + endtime.tv_usec -begintime.tv_usec;

       elapsed_time /= 1000000;             /* 计算执行的时间 */

       printf("This total used time is: %f seconds.\n", elapsed_time);  /* 输出执行时间用于比较 */

       return (void *)0;

}

int main(void)

{

       Job item;

       pthread_t tid1, tid2;

       pthread_rwlock_t q_rwlock;

       int i,err;

       pthread_rwlock_init(&q_rwlock, NULL); /* 初始化读写锁 */

       gettimeofday(&begintime, NULL);         /* 得到程序起始的运行时间 */

       item = (Job)malloc(sizeof(struct job)); /* 初始化链表的头结点 */

       item->next = NULL;

       item->val = 0;

       item->tid = -1;

       if((err=pthread_create(&tid1, NULL, tf10, item)) == -1){ /* 创建第1个线程

                                                                                                            */

              printf("fail to create thread %s\n", strerror(err));

              exit(0);    

       }

       if((err=pthread_create(&tid2, NULL, tf10, item)) == -1){ /* 创建第2个线程

                                                                                                            */

              printf("fail to create thread %s\n", strerror(err));

              exit(0);    

       }

       printf("===the 1st put===\n");

       pthread_rwlock_wrlock(&q_rwlock); /* 锁住链表,向其中插入五个作业结构,使用

                                                                      的是写锁 */

for(i = 0; i < 2; i++){

              if(insert(item, i, tid1) == -1)

                     exit(1);

              if(insert(item, i + 1, tid2) == -1)

                     exit(1);

       }

       if(insert(item, 10, tid1) == -1) /* 再次插入一个结点 */

              exit(1);

       pthread_rwlock_unlock(&q_rwlock); /* 释放写锁 */

       sleep(1);                                            /* 休眠一秒钟,等待两个线程运行 */

       printf("===the 2nd put===\n");

       pthread_rwlock_wrlock(&q_rwlock); /* 再次锁住作业结构队列,使用写锁 */

       if(insert(item, 9, tid2) == -1)

              exit(1);

       pthread_rwlock_unlock(&q_rwlock); /* 释放写锁 */

       err = pthread_join(tid1, NULL);   /* 得到线程1的结束信息 */

       if(err != 0){

              printf("can’t join thread %s\n", strerror(err));

              exit(1);

       }

       pthread_join(tid2, NULL);            /* 得到线程2的结束信息 */

       if(err != 0){

              printf("can’t join thread %s\n", strerror(err));

              exit(1);

       }

       printf("main thread done\n");

       pthread_rwlock_destroy(&q_rwlock); /* 销毁读写锁 */

       return 0;

}

互斥量的使用掌握之后对于读写锁就很好理解了,主要掌握的就是读与写的区别。

时间: 2024-10-13 10:07:54

线程高级操作(二)的相关文章

Android线程管理(二)&mdash;&mdash;ActivityThread

线程通信.ActivityThread及Thread类是理解Android线程管理的关键. 线程,作为CPU调度资源的基本单位,在Android等针对嵌入式设备的操作系统中,有着非常重要和基础的作用.本小节主要从以下三个方面进行分析: <Android线程管理(一)--线程通信> <Android线程管理(二)--ActivityThread>  <Android线程管理(三)--Thread类的内部原理.休眠及唤醒> 二.ActivityThread的主要工作及实现机

线程使用中常见的错误-“System.InvalidOperationException”线程间操作无效: 从不是创建控件“ ”的线程访问它。

"System.InvalidOperationException"类型的未经处理的异常在 System.Windows.Forms.dll 中发生 其他信息: 线程间操作无效: 从不是创建控件"label1"的线程访问它. 解决方法: 1 构造方法中添加 CheckForIllegalCrossThreadCalls = false; 2 方法二 invoke label1.Invoke(new Action<string>((str) => {

C#线程同步技术(二) Interlocked 类

接昨天谈及的线程同步问题,今天介绍一个比较简单的类,Interlocked.它提供了以线程安全的方式递增.递减.交换和读取值的方法. 它的特点是: 1.相对于其他线程同步技术,速度会快很多. 2.只能用于简单的同步问题. 比叫好理解,不再赘述,给一个我们常用的单例模式的 Interlocked 实现: class SourceManager { private SourceManager() { } private static SourceManager sourceManager; publ

Git高级操作

本文是在Git操作指南基础上衍生出来的高级操作,如果你对git不是很熟悉,建议你先阅读Git操作指南. 一.忽略提交特定文件 如果你不想让一些文件上传到git仓库中,可以让Git忽略特定文件或是目录,通过vi ~/.gitconfig编辑git的配置文件,可以使用感叹号(注意英文)来指出例外的情况. 1 !*.a //所有文件后缀为a的都不提交 2 !/demo/ //文件demo下的所有文件都不提交 二.查看仓库历史记录 git log 用来查看仓库历史记录,有三个参数: oneline——把

Linux线程学习(二)

一.Linux进程与线程概述 进程与线程 为什么对于大多数合作性任务,多线程比多个独立的进程更优越呢?这是因为,线程共享相同的内存空间.不同的线程可以存取内存中的同一个变量.所以,程序中的所有线程都可以读或写声明过的全局变量.如果曾用fork() 编写过重要代码,就会认识到这个工具的重要性.为什么呢?虽然fork() 允许创建多个进程,但它还会带来以下通信问题:如何让多个进程相互通信,这里每个进程都有各自独立的内存空间.对这个问题没有一个简单的答案.虽然有许多不同种类的本地IPC (进程间通信)

【转载】8天学通MongoDB——第三天 细说高级操作

今天跟大家分享一下mongodb中比较好玩的知识,主要包括:聚合,游标. 一: 聚合 常见的聚合操作跟sql server一样,有:count,distinct,group,mapReduce. <1> count count是最简单,最容易,也是最常用的聚合工具,它的使用跟我们C#里面的count使用简直一模一样. <2> distinct 这个操作相信大家也是非常熟悉的,指定了谁,谁就不能重复,直接上图. <3> group 在mongodb里面做group操作有点

Android线程管理(二)——ActivityThread

线程通信.ActivityThread及Thread类是理解Android线程管理的关键. 线程,作为CPU调度资源的基本单位,在Android等针对嵌入式设备的操作系统中,有着非常重要和基础的作用.本小节主要从以下三个方面进行分析: <Android线程管理(一)——线程通信> <Android线程管理(二)——ActivityThread> <Android线程管理(三)——Thread类的内部原理.休眠及唤醒> 二.ActivityThread的主要工作及实现机制

winform线程间操作UI的五种方法

经常因为需要在线程间操作UI而头疼,总结了一下,记录出来,以后方便查阅. 方法一 通过设置窗体属性,取消线程间的安全检查.(最简单,最省事,也是最不负责任的一种) 1 public partial class one : Form 2 { 3 public one() 4 { 5 InitializeComponent(); 6 Control.CheckForIllegalCrossThreadCalls = false;//取消线程间的安全检查 7 } 8 9 private void Fo

MySQL学习笔记_9_MySQL高级操作(上)

 MySQL高级操作(上) 一.MySQL表复制 create table t2 like t1;               #复制表结构,t2可以学习到t1所有的表结构 insert into t2 select * from t1;    #复制表数据,但是这样还是会有缺陷,因为没有考虑到列的对应,因为t1与t2的表结构完全一致,所以此次操作才不会出错! 建议: insert into t3(name) select name from t1; #指定复制的列 二.MySQL索引 1.