2016-10-18 11:41:16 MR_Allen_Lwx 阅读数 279
  • linux线程全解-linux应用编程和网络编程第7部分

    本课程讲解linux中线程,首先使用多进程解决上个课程中提出的并发式读取按键和鼠标的任务,然后引出多线程并讲解多线程的优势,后详细讲了多线程的同步技术。学习本课程的目的是学会在linux应用编程中使用多线程技术。

    5046 人正在学习 去看看 朱有鹏

**

Linux多线程与同步:

**
Linux多线程与同步知识点参考下篇文章
作者:Vamei 出处:http://www.cnblogs.com/vamei

线程创建

1、在进程中只有一个控制进程。
2、程序开始运行时每个进程只有一个线程,以单线程方式启动。在创建多个线程以前,进程的行为与传统的进程没有区别。
3、gcc链接时加 -pthread (加在末尾) 链接libpthread.so库(该库时系统的)。
4、创建一个线程调用pthread_create函数。(启动线程)
ldd thr(执行文件名) 此命令可以查看thr这个执行文件相关的库。

线程终止

1、任一线程调用exit函数,整个线程就会终止。
2、信号默认动作是终止进程的,任一线程如接收到此信号,整个进程会终止。(所以要特别注意的是:信号捕捉只写在主线程中,其他线程不写,否则代码维护特别复杂。)
3、单线程退出有三种方式:
1)线程只是从启动函数返回,返回值是线程的退出码。(return的方式)
2)线程可以被同一进程中其他线程取消。 (pthread_cancel)
3)线程调用pthread_exit(voic*) , 可以得到线程的返回值。

线程被分离

使用方法:

 int pthread_detach(pthread_t thread);//使线程处于被分离状态

使用场景:
1、主线程不需要等待子线程
2、主线程不关心子线程的返回码
注意点:
1、对于被分离状态的线程,不需要调用pthread_join等待线程退出。如果其他线程调用了pthread_join,则会失败,返回EINVAL。
2、一个线程不能自己调用pthread_detach改变自己为被分离状态,只能由其他线程调用pthread_detach。

linux多线程常用函数介绍:

http://blog.chinaunix.net/uid-29924858-id-4609393.html
此网站有常用函数使用例子

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                     void *(*start_routine) (void *), void *arg);

返回值:若是成功建立线程返回0,否则返回错误的编号
形式参数:
pthread_t *thread—— 要创建的线程的线程id指针
const pthread_attr_t *attr—— 创建线程时的线程属性
void (*start_routine) (void ) —— 返回值是void类型的指针函数
void *arg —— start_routine 的行参
进行链接的时候要加上-lpthread

int pthread_join(pthread_t thread, void **retval);

功能:pthread_join()函数,以阻塞的方式等待thread指定的线程结束
说明:当函数返回时,被等待线程的资源被收回。如果进程已经结束,那么该函数会立即返回。并且thread指定的线程必须是joinable的。参数含义如下:thread:
线程标识符,即线程ID,标识唯一线程,retval: 用户定义的指针,用来存储被等待线程的返回值,成功时返回0, 失败时返回的则是错误号。可以用于检查?
代码中如果没有pthread_join主线程会很快结束从而使整个进程结束,从而使创建的线程没有机会开始执行就结束了。加入pthread_join后,主线程会一直等待直到等待的线程结束自己才结束,使创建的线程有机会执行。

 pthread_t pthread_self(void);

功能:获取当前调用线程的 thread identifier(标识号)
说明:pthread_t的类型为unsigned long int,所以在打印的时候要使用%lu方式,否则将产生神奇的结果。

void  pthread_exit(void  *retval)

功能:使用函数pthread_exit退出线程,这是线程的主动行为;由于一个进程中的多个线程是共享数据段的,因此通常在线程退出之后,退出线程所占用的资源并不会随着线程的终止而得到释放,但是可以用pthread_join()函数来同步并释放资源。
说明:retval:pthread_exit()调用线程的返回值,可由其他函数如pthread_join来检索获取。

实例

1、利用多线程与有名管道技术,实现两个进程之间发送即时消息,实现聊天功能。

/*
 ============================================================================
 Name        : 双向聊天.c
 Author      : Allen
 Version     :
 Copyright   : Your copyright notice
 Description : Hello World in C, Ansi-style
 ============================================================================
 */

#include <stdio.h>
#include <stdlib.h>
#include<string.h>
#include <unistd.h>
#include<errno.h>
#include<pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

void *readfifo_client1()
{
    int fd = open("/home/vicent/7/fifo1", O_RDONLY);
    if (fd == -1)
    {
        printf("open failed : %s\n", strerror(errno));
    }
    char buf[1024];
    memset(buf, 0, sizeof(buf));
    int len = 0;
    while ((len = read(fd, buf, sizeof(buf))) > 0)
    {
        printf("%s", buf);
        memset(buf, 0, sizeof(buf));
    }
    close(fd);
    return NULL;
}

void *writefifo_client1()
{
    int fd = open("/home/vicent/7/fifo2", O_WRONLY);
    if (fd == -1)
    {
        printf("open failed : %s\n", strerror(errno));
    }
    char buf[1024];
    memset(buf, 0, sizeof(buf));
    while (1)
    {
        memset(buf, 0, sizeof(buf));
        read(STDIN_FILENO, buf, sizeof(buf));
        write(fd, buf, sizeof(buf));
    }
    close(fd);

    return NULL;
}

int main()
{
    pthread_t thd_1, thd_2;
    if (pthread_create(&thd_1, NULL, readfifo_client1, NULL) != 0)
    {
        printf("error is %s\n", strerror(errno));
    }
    if (pthread_create(&thd_2, NULL, writefifo_client1, NULL) != 0)
    {
        printf("error is %s\n", strerror(errno));
    }
    pthread_join(thd_1, NULL);
    pthread_join(thd_2, NULL);
    return 0;
}

/*
 * client2.c
 *
 *  Created on: 2016年10月17日
 *      Author: Allen
 */

#include <stdio.h>
#include <stdlib.h>
#include<string.h>
#include <unistd.h>
#include<pthread.h>
#include<errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

void *readfifo_client2()
{
    int fd = open("/home/vicent/7/fifo2", O_RDONLY);
    if (fd == -1)
    {
        printf("open failed : %s\n", strerror(errno));
    }
    char buf[1024];
    memset(buf, 0, sizeof(buf));
    int len = 0;
    while ((len = read(fd, buf, sizeof(buf))) > 0)
    {
        printf("%s", buf);
        memset(buf, 0, sizeof(buf));
    }
    close(fd);
    return NULL;
}

void *writefifo_client2()
{
    int fd = open("/home/vicent/7/fifo1", O_WRONLY);
    if (fd == -1)
    {
        printf("open failed : %s\n", strerror(errno));
    }
    char buf[1024];
    memset(buf, 0, sizeof(buf));
    while (1)
    {
        memset(buf, 0, sizeof(buf));
        read(STDIN_FILENO, buf, sizeof(buf));
        write(fd, buf, sizeof(buf));
    }
    close(fd);
    return NULL;
}

int main()
{
    pthread_t thd_1, thd_2;
    if (pthread_create(&thd_1, NULL, readfifo_client2, NULL) != 0)
    {
        printf("error is %s\n", strerror(errno));
    }
    if (pthread_create(&thd_2, NULL, writefifo_client2, NULL) != 0)
    {
        printf("error is %s\n", strerror(errno));
    }
    pthread_join(thd_1, NULL);
    pthread_join(thd_2, NULL);
    return 0;
}

makefile

.SUFFIXES: .c .o

CC=gcc

SRCS1=client1.c
SRCS2=client2.c

OBJS1=$(SRCS1:.c=.o)
OBJS2=$(SRCS2:.c=.o)
EXEC1=client1
EXEC2=client2

all: $(OBJS1) $(OBJS2)
    $(CC) -o $(EXEC1) $(OBJS1) -lpthread
    $(CC) -o $(EXEC2) $(OBJS2) -lpthread
    @echo '-------------ok--------------'

.c.o: 
    $(CC) -Wall -g -o $@ -c $<

clean:
    rm -f $(OBJS1)
    rm -f $(OBJS2)
    rm -f core*

结果
这里写图片描述

这里写图片描述

2018-08-26 23:43:54 Qregi 阅读数 537
  • linux线程全解-linux应用编程和网络编程第7部分

    本课程讲解linux中线程,首先使用多进程解决上个课程中提出的并发式读取按键和鼠标的任务,然后引出多线程并讲解多线程的优势,后详细讲了多线程的同步技术。学习本课程的目的是学会在linux应用编程中使用多线程技术。

    5046 人正在学习 去看看 朱有鹏

什么是线程同步?

在多线程编程时,如果变量是只读的,多个线程读取变量时并不会有一致性问题,但是,当一个线程可以修改的变量,其他的线程也可以读或者修改的时候,我们就需要对线程就行同步,确保线程看到的数据必须一致,确保访问变量时不会出现无效的值

Linux对线程提供了几种基本的同步机制

一. 互斥量

互斥量从本质上来说是一把锁,多线程编程时,在访问共享资源时对互斥量进行设置(即加锁),访问完成之后,再释放(解锁)互斥量.

对互斥量进行加锁之后,任何试图再次对互斥量进行加锁的线程都会被阻塞直到当前线程释放该互斥锁.

如果释放时有一个以上的线程阻塞,那么该锁上的阻塞线程都会变成可运行状态,第一个变为运行的线程就可以对互斥量进行加锁,其他线程看到互斥锁依然是锁着的时候就只能回去再次重新阻塞等待.

互斥变量使用pthread_mutex_t数据类型表示的,使用互斥变量之前,需要对它进行初始化

·可以把互斥量设置为PTHREAD_MUTEX_INITIALIZER(使用于静态分配的互斥量)

·可以调用pthread_mutex_init函数进行初始化,如果动态分配了互斥量,在释放内存前需要调用pthread_mutex_destroy函数

销毁互斥量时需要注意:

·使用PTHREAD_MUTEX_INITIALIZER初始化的互斥量不需要销毁

·不要销毁一个已经加锁了的互斥量

·已经销毁的互斥量,要确保后面不会再有线程尝试加锁

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

int pthread_mutex_destroy(pthread_mutex_t* mutex);

对互斥量加锁解锁

·对互斥量进行加锁操作需要调用pthread_mutex_lock函数

·对互斥量进行解锁需要调用pthread_mutex_unlock函数

·如果线程不希望加锁时被阻塞,可以使用pthread_mutex_trylock函数尝试对互斥量进行加锁.如果调用时互斥量处于未锁状态,则这个函数会锁住互斥量,不会阻塞等待,否则pthread_mutex_trylock就会失败,不能锁住互斥量,返回EBUSY

举个栗子:

在主函数中,对全局变量互斥量mutex进行初始化,然后再创建四个线程,然后线程等待,最后再末尾调用pthread_mutex_destroy函数

四个线程使用同一个线程处理函数,每个线程都是一个循环,循环内部对操作进行加锁和解锁

注意,操作互斥量时,可能会出现死锁的情况

情况一:如果一个线程对同一个互斥量进行加锁两次,那么它就会陷入死锁状态,因为想要对互斥量加锁,必须保证互斥量没有被锁,不然就只能等待,当我加第二次锁的时候,发现互斥量已经加锁了,就阻塞的等待,但是因为没有解锁,就会一直等下去

情况二:程序中使用一个以上的互斥量时,如果允许一个线程一直占有第一个互斥量,并且在试图锁住第二个互斥量时处于阻塞状态,但是拥有第二个互斥量的线程也在试图锁住第一个互斥量.就造成两个线程都无法向前运行,就会产生死锁

二. 读写锁

读写锁(reader-writer lock)与互斥量类似,不过读写锁允许更高的并行性

互斥量只有两种状态,要么是不加锁状态,要么就是锁状态,而且一次只有一个线程可以对其加锁

读写锁有三个状态:

①读模式下的加锁状态

②写模式下的加锁状态

③不加锁状态

读写锁的特征:

·一次只有一个线程可以占有写模式的读写锁,但是可以有多个线程占有读模式的读写锁

·当读写锁是写加锁状态时,在这个锁被解锁之前,所有试图对这个锁加锁的线程都会被阻塞.

·当读写锁是读加锁状态时,所有试图以读模式对它进行加锁的线程都可以访问,但是任何以希望以写模式对锁进行加锁的线程都会被阻塞,直到所有线程释放他们的读锁

·需要注意的一点.当读写锁处于读模式的状态时,线程试图以写模式获取上锁时,读写锁会阻塞后面的读模式的锁请求,这样可以避免读模式的锁被长期占用.

·读写锁适合于对数据结构读的次数远大于写的情况

·读写锁也叫共享互斥锁,当读写锁以读模式锁住时,就可以说成是以共享模式锁住的,当读写锁以写模式锁住时,就可以说成是以互斥模式锁住的

读写锁的初始化和销毁函数:

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

int pthread_rwlock_destroy(pthread_rwlock_t* rwlock);

两个函数的返回值:成功返回0,否则返回错误编号

读写锁的加锁和解锁:

读模式加锁
int pthread_rwlock_rdlock(pthread_rwlock_t* rwlock);

写模式加锁
int pthread_rwlock_wrlock(pthread_rwlock_t* rwlock);

解锁
int pthread_rwlock_unlock(pthread_rwlock_t* rwlock);

所有函数的返回值:成功返回0.失败返回错误编号

读写锁也有条件版本:


int pthread_rwlock_tryrdlock(pthread_rwlock_t* rwlock);

int pthread_rwlock_trywrlock(pthread_rwlock_t* rwlock);

所有函数的返回值:成功返回0.失败返回错误编号

三. 条件变量

条件变量是线程可用的另一种同步机制,条件变量给多线程提供了一个会和的场所.条件变量与互斥量一起使用时,互斥锁用于上锁,条件变量则用于等待,运行线程以无竞争的方式等待特定的条件发生

条件变量本身是由互斥量保护的,线程在改变条件变量之前必须先锁住互斥量.其他线程在获得互斥量之前不会察觉到这种变化,因为互斥量必须是在锁定之后才能计算条件

条件变量是由pthread_cond_t数据类型表示的,有两种初始化方式,一种是把常量PTHREAD_COND_INITIALIZER赋给静态分配的条件变量,另一种是使用初始化函数

条件变量初始化函数

int pthread_cond_init(pthread_cond_t *restrict cond,
                      const pthread_condattr_t *rest rict attr);

参数:    
cond:要初始化的条件变量    
attr:NULL 

销毁函数

int pthread_cond_destroy(pthread_cond_t *cond);

使用pthread_cond_wait等待条件变量变为真

int pthread_cond_wait(pthread_cond_t *restrict cond,
                      pthread_mutex_t *restrict mute x); 

参数:    
cond:要在这个条件变量上等待    
mutex:互斥量

调用者给pthread_cond_wait函数传入一个互斥量mutex,这个互斥量会对条件进行保护,这个函数自动把调用线程放到等待条件的线程列表上,对互斥量进行解锁,然后等待条件变量触发.这是线程挂起,不占用CPU时间,直到条件变量被触发.这个函数返回前会自动重新给互斥量加锁

互斥量的解锁和在条件变量上挂起都是自动进行的.因此条件变量触发之前,所有线程都要对互斥量进行加锁.

唤醒等待

int pthread_cond_broadcast(pthread_cond_t *cond);   
int pthread_cond_signal(pthread_cond_t *cond);

这两个函数用于通知线程条件已满足,pthread_cond_signal函数至少能唤起一个等待的线程,而pthread_cond_broadcast则可以唤起所有等待该条件的线程

举个栗子,创建两个线程,分别打印奇数和偶数

在两个线程处理函数之中,比如处理偶数的函数中,如果当前value值是偶数,就打印并把value自增1,然后使用pthread_cond_signal函数通知条件变量odd,如果不为偶数,就使用pthread_cond_wait函数阻塞的等待

这里也是同样的做法

这是最后的结果:

使用条件变量的优点:

可以避免线程竞争,由于线程之间是异步进行的,所以每个线程得到锁的概率依照线程的优先级来决定,如果想要让某个线程执行特定的任务,就可以利用条件变量,让系统立即调度拥有特定条件变量的线程,避免线程竞争

可以提高效率,如果一个线程得到锁,那么其他的线程得不到锁,就会不停的去查询锁有没有被释放,这样会浪费大量的资源,利用条件变量可以使得不到锁的线程处于阻塞的等待状态,直到被使用pthread_cond_signal函数通知,可以提高效率

四. POSIX信号量

POSIX信号量和SystemV信号量的作用相同,都是用于同步操作,达到无冲突的访问共享资源的目的,但是POSIX信号量可以用于线程的同步

创建一个信号量,创建的时候还要求初始化信号量的值

// 无名信号量的初始化和销毁

int sem_init(sem_t* sem, int pshared, unsigned int value);

参数:
pshared,0表示线程共享,非0表示进程共享
value: 信号量初始值

int sem_destroy(sem_t* sem);

sem_init() 用于无名信号量的初始化。无名信号量在初始化前一定要子啊内存中分配一个 sem_t 信号量类型,这就是无名信号量又称为基于内存的信号量的原因。

// 有名信号量的创建删除

sem_t *sem_open(const char *name, int oflag);
sem_t *sem_open(const char *name, int oflag,
                  mode_t mode, unsigned int value);

oflag 参数可以为:0 , O_CREAT ,O_EXCL ,如果为 0 表示打开一个已存在的信号量,如果为 O_CREAT ,表示如果信号量不存在就创建一个信号量,如果存在则打开被返回。此时 mode 和 value 需要指定。如果为 O_CREAT | O_EXCL ,表示如果信号量已存在会返回错误。

mode 参数用于创建信号量时,表示信号量的权限位,和 open 函数一样包括:S_IRUSR , S_IWUSR ,S_IRGRP ,S_IWGRP ,S_IROTH ,S_IWOTH 

value 表示创建信号量时,信号量的初始值。

等待信号量, 该操作会检查信号量的值,如果其值小于或等于0 ,那就阻塞,知道该值变成大于 0 ,然后等待进程将信号量的值减 1 ,进程获得共享资源的访问权限.这整个操作必须是一个原子操作.

int sem_wait(sem_t* sem);

#ifdef __USE_XOPEN2K
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
#endif

int sem_trywait (sem_t * sem);

发布信号量.该操作将信号量的值加 1 ,如果有进程阻塞着等待该信号量,那么其中一个进程将被唤醒.该操作也必须是一个原子操作

int sem_post(sem_t* sem);

获取当前信号量的值

int sem_getvalue(sem_t *sem,  int *sval);

 //成功返回0,失败返回-1

五. 自旋锁

自旋锁(spinlock)与互斥量 类似,但是它不是通过休眠使进程阻塞,而是在获取锁之前一直处于忙等阻塞状态.

自旋锁可以用于以下情况:锁被持有的时间端,而且线程并不希望在重新调度上花太多的成本

自旋锁用于多个CPU系统中,在单处理器系统中,自旋锁不起锁的作用,只是禁止或启用内核抢占.在自旋锁忙等待期间,内核抢占机制还是有效的,等待自旋锁释放的线程可能被更高优先级的线程抢占CPU

自旋锁基于共享变量。一个线程通过给共享变量设置一个值来获取锁,其他等待线程查询共享变量是否为0来确定锁现是否可用,然后在忙等待的循环中"自旋"直到锁可用为止

自旋锁的接口与互斥量类似,这使得它可以比较容易的从一个替换为另一个.可以用pthread_spin_init函数对自旋锁进行初始化,使用pthread_spin_destroy函数进行自旋锁的反复初始化

自旋锁的初始化

int pthread_spin_init(pthread_spinlock_t* lock, int pshared);

int pthread_spin_destroy(pthread_spinlock_t* lock);

两个函数的返回值:若成功返回0,,失败返回错误编号

自旋锁初始化函数中的参数pshared表示进程共享属性,表明自旋锁是如何获取的.

如果pshared被设为PTHREAD_PROCESS_SHARED,则自旋锁可以被访问锁底层的内存的线程获取,即使线程属于不同的进程

如果被设置为PTHREAD_PROCESS_PRIVATE,自旋锁就只能被初始化该锁的进程内部的线程获取

自旋锁的加锁和解锁

int pthread_spin_lock(pthread_spinlokc_t* lock);

int pthread_spin_trylock(pthread_spinlokc_t* lock);

int pthread_spin_unlock(pthread_spinlokc_t* lock);

自旋锁的注意事项:

1.临界区代码不要存在睡眠情况(主要因为发生睡眠不可预知睡眠多长时间,另外长时间睡眠,导致即将进入临界区其他线程,长时间得不到自旋锁,无休止自旋,从而导致死锁),所以临界区调用导致睡眠函数,不能选择自旋锁。

2.保证进入临界区的线程,不发生内核抢占。(这一点不必担心,持有自旋锁情况,Linux内核不进行抢占)

3.临界区代码,执行时间不能太长。(因为其他线程,如果要进入话,导致自旋,过多消耗CPU资源)

4.选择自旋锁时,也要注意中断情况(上半部分中断(硬件中断)和下半部分中断(软中断),中断会抢占即中断到来时,打断目前临界区代码执行,转往执行中断代码),当中断要进入自旋锁保护临界区代码时,将导致线程与中断发生死锁可能。

六. 屏障

屏障(barrier)是用户协调多个线程并行工作的同步机制.屏障允许每个线程等待,直到所有的合作线程都达到某一点,然后从该点继续执行.

屏障的初始化和销毁

int pthread_barrier_destroy(pthread_barrier_t *barrier);

int pthread_barrier_init(pthread_barrier_t *restrict barrier,
              const pthread_barrierattr_t *restrict attr, unsigned count);

两个函数的返回值:若成功,返回0;否则,返回错误编号

初始化屏障时, 可以使用count参数指定, 在允许所有线程继续运行之前, 必须到达屏障的线程数目. 屏障属性attr设置为NULL表示使用默认属性.

等待其他线程

int pthread_barrier_wait(pthread_barrier_t *barrier);

返回值:若成功,返回0或者PTHREAD_BARRIER_SERIAL_THREAD;否则,返回错误编号

调用pthread_barrier_wait的线程在屏障技术count未满足条件时,会进入休眠状态.如果该线程是最后一个调用pthread_barrier_wait的线程,就满足了屏障计数,所有的线程都被唤醒.

对于一个任意线程,pthread_barrier_wait函数返回PTHREAD_BARRIER_SERIAL_THREAD.剩下的线程看到的返回值是0,这使得一个线程可以作为主线程,它可以工作在其他所有线程已完成的工作结果上.

 

参考的一部分博客:

https://blog.csdn.net/lifuxin73/article/details/51637837

https://blog.csdn.net/xiaofei0859/article/details/23629287

 

2015-06-16 20:46:05 YuZhiHui_No1 阅读数 804
  • linux线程全解-linux应用编程和网络编程第7部分

    本课程讲解linux中线程,首先使用多进程解决上个课程中提出的并发式读取按键和鼠标的任务,然后引出多线程并讲解多线程的优势,后详细讲了多线程的同步技术。学习本课程的目的是学会在linux应用编程中使用多线程技术。

    5046 人正在学习 去看看 朱有鹏

        多线程同步,当有多个线程同时访问共享内存时就会产生数据不一致性。所以为了保证数据的一致性必须让线程同步,同步方式有下面几种:

互斥量

        互斥量从本质上来说是一把锁,在访问共享资源前对互斥量进行加锁,访问完后释放互斥量上的锁。对互斥量加锁后,任何其他的线程对互斥量加锁都会被阻塞,直到互斥量上的锁释放。当锁释放后,阻塞的所有线程都会变成可运行的,然后进行抢占。

        互斥量用pthread_mutex_t数据类型来表示,在使用互斥量之前要先对其进行初始化。

        静态初始化: pthread_mutex_t   mutex  =  PTHREAD_MUTEX_INITIALIZER

        动态初始化:int pthread_mutex_init(pthread_mutex_t  *mutex,  const pthread_mutexattr_t *attr);

        注销互斥量:int pthread_mutex_destroy(pthread_mutex_t  *mutex);

        返回值:若成功则返回0,失败则返回错误码;

        第一个参数:是互斥量的锁;注销互斥量函数的第二个参数为锁的属性,一般为NULL;


        初始化后就准备对互斥量上锁了,如果互斥量已经上锁了,那试图上锁的线程将会阻塞直到锁释放。上锁函数如下:

        int pthread_mutex_lock(pthread_mutex_t  *mutex);

        int pthread_mutex_trylock(pthread_mutex_t  *mutex);

        int pthread_mutex_unlock(pthead_mutex_t   *mutex);

        返回值:成功返回0,失败返回错误码;

        pthread_mutex_lock()函数对已经上锁的互斥量上锁会阻塞  直到锁释放。

        pthread_mutex_trylock()函数则不阻塞等待,如果互斥量没有上锁,则上锁返回0;否则,失败返回错误码。


读写锁

        互斥量虽然能有效的保证数据的一致性,但是这降低了数据的并行访问效率。因为互斥量只有两个状态,一个是上锁状态,一个是不上锁状态。如果多个线程都是只读不写,那么如果用互斥量就有点损失性能了。所以也就出现了读写锁,也就是共享-独占锁,当读写锁以读模式锁住,他则是共享的,其他读线程都可以再次上锁访问,这是共享锁。如果以写模式锁住,则是独占锁,这时和互斥量一样了。

        下面是读写锁初始化函数:

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

        int  pthread_rwlock_destroy(pthread_rwlock_t  *rwlock);

        返回为0, 失败返回错误码;

 

        在读模式下上读写锁函数:int pthread_rwlock_rdlock(pthread_rwlock_t  *rwlock);

        在写模式下上读写锁函数:int pthread_rwlock_wrlock(pthread_rwlock_t  *rwlock);

        不管在什么模式下,都使用同一个函数释放锁:int pthread_rwlock_unlock(pthread_rwlock_t  *rwlock);

        返回值:成功返回0,失败返回错误码;

        这里读模式就是上读锁的意思,表示该线程只读临界区的数据,而不修改。写模式则是独占锁,可能会修改数据;

        注意:读写锁不像互斥量,读写锁是不会阻塞的。


         和互斥量一样,读写锁也有尝试加锁函数:

         int pthread_rwlock_tryrdlock(pthread_rwlock_t  *rwlock); //读

         int pthread_rwlock_trywrlock(pthread_rwlock_t  *rwlock); //写

         返回值:成功返回0,失败返回错误码;

         其实在读、写模式下的函数已经不再阻塞了,本来可以不用尝试加锁函数的,但是共享锁可能有线程数目的限制,所以可以用尝试加锁来加锁;


条件变量

        条件变量是多线程下用来同步的另外一种机制。条件变量给多线程提供一个会合的场所,可以和互斥量一起使用,允许线程以无竞争的方式等待特定的条件发生。

        条件变量本身是由互斥量保护的,线程在改变条件前必须要锁住互斥量,其他线程因为没有得到锁,所以不知道条件的改变。

       

        条件变量初始化:

        静态初始化:pthread_cond_t  cond = PTHREAD_COND_INITIALIZER;

        动态初始化:pthread_cond_t  cond;

                                int pthread_cond_init(pthread_cond_t  *cond,  pthread_condattr_t  *attr);//初始化函数

                                int pthread_cond_destroy(pthread_cond_t  *cond);//注销条件函数

        返回值:成功返回0,失败返回错误码;

        初始化函数: pthread_cond_init()中的第二个参数为条件变量属性,一般设置NULL;


        条件变量等待函数:

        int  pthread_cond_wait(pthread_cond_t  *cond,  pthread_mutex_t  *mutex);

        int  pthread_cond_timedwait(pthread_cond_t  *cond,  pthread_mutex_t  *mutex, const  struct  timespec  *timeout);

        返回值:成功返回0,失败返回错误码;

        pthread_cond_wait()和pthread_cond_timedwait()函数类似,只是pthread_cond_timedwait()函数有个定时功能,如果在规定时间内条件没有变真,pthread_cond_wait()函数会一直睡眠(他没有时间概念);而pthread_cond_timedwait()则会醒来直接返回错误码。所以这里只分析pthread_cond_wait()函数;

        当线程调用了pthread_cond_wait()函数,则线程会在等待条件的线程列表中休眠,然后解互斥量的锁(等待条件有个隐藏的互斥量的锁),这两个操作都是原子操作。

        为什么要解开互斥量的锁呢?因为调用线程在等待条件变量的改变(要不然他一直睡眠着),如果他又一直锁着,其他线程则无法获取到锁(这是互斥量锁,独占的)也就无法修改条件变量,那么等待线程(也就是他自己)就会死锁了。        

        为什么要原子操作呢?其实pthread_cond_wait()函数应该是这样的,先进入条件变量的等待线程列表中,然后检查条件,不符合,则解开互斥量的锁,睡眠(不知道是先解锁还是先睡眠,但是先检查条件是否符合是肯定的)。所以这一系列操作要用原子操作,因为如果不是原子操作当检查完条件时,其他线程又更改了条件,使得条件满足了。但该线程刚检查完不符合,那么就会解锁睡眠等待了,可其他线程已经更改了条件。这就会造成死锁了。

        当条件符合后,pthread_cond_wait()就会醒来,在该函数返回时,互斥量会被再次被锁住。


        条件变量唤醒函数:

        int  pthread_cond_signal(pthread_cond_t  *cond);// 唤醒条件变量上所有的等待线程

        int  pthread_cond_broadcast(pthread_cond_t  *cond);//唤醒条件变量上的某个线程

        返回值:成功返回0,失败返回错误码;

      

        实例查看: 多线程的生产者和消费者问题

        转载地址:http://blog.csdn.net/yuzhihui_no1/article/details/46523251

 

2018-05-29 16:17:26 coolwriter 阅读数 288
  • linux线程全解-linux应用编程和网络编程第7部分

    本课程讲解linux中线程,首先使用多进程解决上个课程中提出的并发式读取按键和鼠标的任务,然后引出多线程并讲解多线程的优势,后详细讲了多线程的同步技术。学习本课程的目的是学会在linux应用编程中使用多线程技术。

    5046 人正在学习 去看看 朱有鹏

一、pthread_create

原型:int pthread_create(pthread_t *thread,

                                         const pthread_attr_t *attr,

                                         void *(*start_routine)(void *),void *arg)  

头文件:<pthread.h>,编译时需要链接pthread库  
功能:创建新的线程,成功返回0,失败返回错误编号  
参数:thread:用来存储新创建的线程id  
          attr:一个指向pthread_attr_t结构的指针,指向的结构决定了新创建的线程的属性,如优先级等。可为NULL,表示采用默认属性。  
          start_routine:函数指针。创建线程与创建进程后的执行情况不同。创建进程后,子进程顺着代码执行下去,而创建线程,线程是从一个指定的函数入口开始执行。start_routine指向了线程的函数入口  

            arg:线程函数入口需要一个参数void *,arg就是该参数,可为NULL  

多进程程序中,父进程的退出不会导致子进程的退出;而在多线程程序中,进程结束时,该进程所创建的线程也会结束运行。所以多线程程序中进程需要等待其线程结束才能结束。


二、pthread_join

原型:int pthread_join(pthread_t thread,void *retval)  
头文件:<pthread.h>编译时需要链接pthread库  
功能:等待线程结束,成功返回0,失败返回错误编号  
参数:thread:要等待结束的线程的id  

     retval:保存线程结束时的状态,一般为NULL  

三、pthread_exit

原型:void pthread_exit(void *retval)  
头文件:<pthread.h>编译时需要链接pthread库  
功能:结束线程  
参数:retval:线程返回的值,可与pthread_join中的参数retval配合使用,但一般设为NULL  


在实际应用中,多个线程往往会访问同一数据或资源,为避免线程之间相互影响,需要引入线程互斥机制,而互斥锁是互斥机制中的一种。


四、pthread_mutex_init


原型:int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr)  
头文件:<pthread.h>  
功能:初始化互斥锁,成功返回0,失败返回错误编号  
参数:mutex:要初始化的互斥锁的指针  
     attr:用于设置互斥锁的属性,一般设为NULL,表示默认属性  



五、pthread_mutex_lock


原型:int pthread_mutex_lock(pthread_mutex_t *mutex)  
头文件:<pthread.h>  
功能:获取互斥锁,成功返回0,失败返回错误编号  

参数:mutex:要获取的互斥锁的指针  


六、pthread_mutex_unlock

原型:int pthread_mutex_unlock(pthread_mutex_t *mutex)  
头文件:<pthread.h>  
功能:释放互斥锁,成功返回0,失败返回错误编号  

参数:mutex:要获取的互斥锁的指针  



#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void *thread_function(void *arg);

char message[] = "Hello World";

int main()
{
    int res;
    pthread_t a_thread;
    void *thread_result;

    res = pthread_create(&a_thread, NULL, thread_function, (void *)message);
    if (res != 0)
    {
        perror("Thread creation failed!");
        exit(EXIT_FAILURE);
    }

    printf("Waiting for thread to finish.../n");
    
    res = pthread_join(a_thread, &thread_result);
    if (res != 0)
    {
        perror("Thread join failed!/n");
        exit(EXIT_FAILURE);
    }

    printf("Thread joined, it returned %s/n", (char *)thread_result);
    printf("Message is now %s/n", message);

    exit(EXIT_FAILURE);
}

void *thread_function(void *arg)
{
    printf("thread_function is running. Argument was %s/n", (char *)arg);
    sleep(3);
    strcpy(message, "Bye!");
    pthread_exit("Thank you for your CPU time!");
}

线程互斥

线程互斥:进程创建出两个线程,两个线程互斥

#include<stdio.h>  
#include<pthread.h> 
#include<unistd.h>  
pthread_mutex_t mutex;  
pthread_t thread[2];  
int number=0;  
void* work1(void *)  
{  
    int i;  
    for(i=0;i<10;i++)  
    {  
        pthread_mutex_lock(&mutex);  
        number++;  
        printf("work1:%d\n",number);  
        pthread_mutex_unlock(&mutex);  
        sleep(1);  
    }  
    pthread_exit(NULL);  
}  
void* work2(void *)  
{  
    int i;  
    for(i=0;i<10;i++)  
    {  
        pthread_mutex_lock(&mutex);  
        number++;  
        printf("work2:%d\n",number);  
        pthread_mutex_unlock(&mutex);  
        sleep(1);  
    }  
    pthread_exit(NULL);  
}  
int main()  
{  
    /*初始化互斥锁*/  
    pthread_mutex_init(&mutex,NULL);  
    /*创建线程1*/  
    pthread_create(&thread[0],NULL,work1,NULL);  
    /*创建线程2*/  
    pthread_create(&thread[1],NULL,work2,NULL);  
    /*等待线程1结束*/  
    pthread_join(thread[0],NULL);  
    /*等待线程2结束*/  
    pthread_join(thread[1],NULL); 
    return 0;
}   

线程同步

多个线程按照规定的顺序执行,即为线程同步。线程同步可利用全局变量来实现,但这会导致执行效率低下,应采用专用的函数来实现同步。

  1. 初始化:pthread_cond_t cond_ready=PTHREAD_COND_INITIALIZER;  
  2. 等待条件成熟:pthread_cond_wait(&cond_ready, &mut);  
  3. 设置条件成熟:pthread_cond_signal(&cond_ready);

线程同步:进程创建了两个线程,其中线程work2必须在线程work1执行完后才执行

#include<stdio.h>  
#include<pthread.h>  
#include<unistd.h>  
pthread_mutex_t mutex;  
pthread_t thread[2];  
int number=0;  
pthread_cond_t cond_ready=PTHREAD_COND_INITIALIZER;  
void* work1(void *)  
{  
    while(1)  
    {  
        pthread_mutex_lock(&mutex);  
        number++;  
        printf("work1:%d\n",number);  
        pthread_mutex_unlock(&mutex);  
        if(number==5)  
        {  
            pthread_cond_signal(&cond_ready);/*发送信号,说明条件成熟*/  
            break;  
        }  
        sleep(1);  
    }  
    pthread_exit(NULL);  
}  
void* work2(void *)  
{  
    pthread_mutex_lock(&mutex);  
    if(number<5)  
    {  
        pthread_cond_wait(&cond_ready, &mutex);/*需要配合着互斥锁使用,当条件不成熟时会自动释放互斥锁,从而使其它进程能够获取互斥锁*/  
    }  
    number=0;  
    printf("work2:%d\n",number);  
    pthread_mutex_unlock(&mutex);  
    pthread_exit(NULL);  
}  
int main()  
{  
    /*初始化互斥锁*/  
    pthread_mutex_init(&mutex,NULL);  
    /*创建线程1*/  
    pthread_create(&thread[0],NULL,work1,NULL);  
    /*创建线程2*/  
    pthread_create(&thread[1],NULL,work2,NULL);  
    /*等待线程1结束*/  
    pthread_join(thread[0],NULL);  
    /*等待线程2结束*/  
    pthread_join(thread[1],NULL);  
    return 0;  
}

2017-10-23 14:53:12 renlonggg 阅读数 198
  • linux线程全解-linux应用编程和网络编程第7部分

    本课程讲解linux中线程,首先使用多进程解决上个课程中提出的并发式读取按键和鼠标的任务,然后引出多线程并讲解多线程的优势,后详细讲了多线程的同步技术。学习本课程的目的是学会在linux应用编程中使用多线程技术。

    5046 人正在学习 去看看 朱有鹏

原文地址:http://blog.csdn.net/zsf8701/article/details/7844316

线程的最大特点是资源的共享性,但资源共享中的同步问题是多线程编程的难点。linux下提供了多种方式来处理线程同步,最常用的是互斥锁、条件变量和信号量。

一、互斥锁(mutex)

通过锁机制实现线程间的同步。

  1. 初始化锁。在Linux下,线程的互斥量数据类型是pthread_mutex_t。在使用前,要对它进行初始化。
    静态分配:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    动态分配:int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutex_attr_t *mutexattr);
  2. 加锁。对共享资源的访问,要对互斥量进行加锁,如果互斥量已经上了锁,调用线程会阻塞,直到互斥量被解锁。
    int pthread_mutex_lock(pthread_mutex *mutex);
    int pthread_mutex_trylock(pthread_mutex_t *mutex);
  3. 解锁。在完成了对共享资源的访问后,要对互斥量进行解锁。
    int pthread_mutex_unlock(pthread_mutex_t *mutex);
  4. 销毁锁。锁在是使用完成后,需要进行销毁以释放资源。
    int pthread_mutex_destroy(pthread_mutex *mutex);
  1. #include <cstdio>  
  2. #include <cstdlib>  
  3. #include <unistd.h>  
  4. #include <pthread.h>  
  5. #include "iostream"  
  6. using namespace std;  
  7. pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;  
  8. int tmp;  
  9. void* thread(void *arg)  
  10. {  
  11.     cout << "thread id is " << pthread_self() << endl;  
  12.     pthread_mutex_lock(&mutex);  
  13.     tmp = 12;  
  14.     cout << "Now a is " << tmp << endl;  
  15.     pthread_mutex_unlock(&mutex);  
  16.     return NULL;  
  17. }  
  18. int main()  
  19. {  
  20.     pthread_t id;  
  21.     cout << "main thread id is " << pthread_self() << endl;  
  22.     tmp = 3;  
  23.     cout << "In main func tmp = " << tmp << endl;  
  24.     if (!pthread_create(&id, NULL, thread, NULL))  
  25.     {  
  26.         cout << "Create thread success!" << endl;  
  27.     }  
  28.     else  
  29.     {  
  30.         cout << "Create thread failed!" << endl;  
  31.     }  
  32.     pthread_join(id, NULL);  
  33.     pthread_mutex_destroy(&mutex);  
  34.     return 0;  
  35. }  
  36. //编译:g++ -o thread testthread.cpp -lpthread  

二、条件变量(cond)

互斥锁不同,条件变量是用来等待而不是用来上锁的。条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。通常条件变量和互斥锁同时使用。条件变量分为两部分: 条件和变量。条件本身是由互斥量保护的。线程在改变条件状态前先要锁住互斥量。条件变量使我们可以睡眠等待某种条件出现。条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使"条件成立"(给出条件成立信号)。条件的检测是在互斥锁的保护下进行的。如果一个条件为假,一个线程自动阻塞,并释放等待状态改变的互斥锁。如果另一个线程改变了条件,它发信号给关联的条件变量,唤醒一个或多个等待它的线程,重新获得互斥锁,重新评价条件。如果两进程共享可读写的内存,条件变量可以被用来实现这两进程间的线程同步。

  1. 初始化条件变量。
    静态态初始化,pthread_cond_t cond = PTHREAD_COND_INITIALIER;
    动态初始化,int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
  2. 等待条件成立。释放锁,同时阻塞等待条件变量为真才行。timewait()设置等待时间,仍未signal,返回ETIMEOUT(加锁保证只有一个线程wait)
    int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
    int pthread_cond_timewait(pthread_cond_t *cond,pthread_mutex *mutex,const timespec *abstime);
  3. 激活条件变量。pthread_cond_signal,pthread_cond_broadcast(激活所有等待线程)
    int pthread_cond_signal(pthread_cond_t *cond);
    int pthread_cond_broadcast(pthread_cond_t *cond); //解除所有线程的阻塞
  4. 清除条件变量。无线程等待,否则返回EBUSY
    int pthread_cond_destroy(pthread_cond_t *cond);
  1. #include <stdio.h>  
  2. #include <pthread.h>  
  3. #include "stdlib.h"  
  4. #include "unistd.h"  
  5. pthread_mutex_t mutex;  
  6. pthread_cond_t cond;  
  7. void hander(void *arg)  
  8. {  
  9.     free(arg);  
  10.     (void)pthread_mutex_unlock(&mutex);  
  11. }  
  12. void *thread1(void *arg)  
  13. {  
  14.     pthread_cleanup_push(hander, &mutex);  
  15.     while(1)  
  16.     {  
  17.         printf("thread1 is running\n");  
  18.         pthread_mutex_lock(&mutex);  
  19.         pthread_cond_wait(&cond, &mutex);  
  20.         printf("thread1 applied the condition\n");  
  21.         pthread_mutex_unlock(&mutex);  
  22.         sleep(4);  
  23.     }  
  24.     pthread_cleanup_pop(0);  
  25. }  
  26. void *thread2(void *arg)  
  27. {  
  28.     while(1)  
  29.     {  
  30.         printf("thread2 is running\n");  
  31.         pthread_mutex_lock(&mutex);  
  32.         pthread_cond_wait(&cond, &mutex);  
  33.         printf("thread2 applied the condition\n");  
  34.         pthread_mutex_unlock(&mutex);  
  35.         sleep(1);  
  36.     }  
  37. }  
  38. int main()  
  39. {  
  40.     pthread_t thid1,thid2;  
  41.     printf("condition variable study!\n");  
  42.     pthread_mutex_init(&mutex, NULL);  
  43.     pthread_cond_init(&cond, NULL);  
  44.     pthread_create(&thid1, NULL, thread1, NULL);  
  45.     pthread_create(&thid2, NULL, thread2, NULL);  
  46.     sleep(1);  
  47.     do  
  48.     {  
  49.         pthread_cond_signal(&cond);  
  50.     }while(1);  
  51.     sleep(20);  
  52.     pthread_exit(0);  
  53.     return 0;  
  54. }  
  1. #include <pthread.h>  
  2. #include <unistd.h>  
  3. #include "stdio.h"  
  4. #include "stdlib.h"  
  5. static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;  
  6. static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;  
  7. struct node  
  8. {  
  9.     int n_number;  
  10.     struct node *n_next;  
  11. }*head = NULL;  
  12.   
  13. static void cleanup_handler(void *arg)  
  14. {  
  15.     printf("Cleanup handler of second thread./n");  
  16.     free(arg);  
  17.     (void)pthread_mutex_unlock(&mtx);  
  18. }  
  19. static void *thread_func(void *arg)  
  20. {  
  21.     struct node *p = NULL;  
  22.     pthread_cleanup_push(cleanup_handler, p);  
  23.     while (1)  
  24.     {  
  25.         //这个mutex主要是用来保证pthread_cond_wait的并发性  
  26.         pthread_mutex_lock(&mtx);  
  27.         while (head == NULL)  
  28.         {  
  29.             //这个while要特别说明一下,单个pthread_cond_wait功能很完善,为何  
  30.             //这里要有一个while (head == NULL)呢?因为pthread_cond_wait里的线  
  31.             //程可能会被意外唤醒,如果这个时候head != NULL,则不是我们想要的情况。  
  32.             //这个时候,应该让线程继续进入pthread_cond_wait  
  33.             // pthread_cond_wait会先解除之前的pthread_mutex_lock锁定的mtx,  
  34.             //然后阻塞在等待对列里休眠,直到再次被唤醒(大多数情况下是等待的条件成立  
  35.             //而被唤醒,唤醒后,该进程会先锁定先pthread_mutex_lock(&mtx);,再读取资源  
  36.             //用这个流程是比较清楚的  
  37.             pthread_cond_wait(&cond, &mtx);  
  38.             p = head;  
  39.             head = head->n_next;  
  40.             printf("Got %d from front of queue/n", p->n_number);  
  41.             free(p);  
  42.         }  
  43.         pthread_mutex_unlock(&mtx); //临界区数据操作完毕,释放互斥锁  
  44.     }  
  45.     pthread_cleanup_pop(0);  
  46.     return 0;  
  47. }  
  48. int main(void)  
  49. {  
  50.     pthread_t tid;  
  51.     int i;  
  52.     struct node *p;  
  53.     //子线程会一直等待资源,类似生产者和消费者,但是这里的消费者可以是多个消费者,而  
  54.     //不仅仅支持普通的单个消费者,这个模型虽然简单,但是很强大  
  55.     pthread_create(&tid, NULL, thread_func, NULL);  
  56.     sleep(1);  
  57.     for (i = 0; i < 10; i++)  
  58.     {  
  59.         p = (struct node*)malloc(sizeof(struct node));  
  60.         p->n_number = i;  
  61.         pthread_mutex_lock(&mtx); //需要操作head这个临界资源,先加锁,  
  62.         p->n_next = head;  
  63.         head = p;  
  64.         pthread_cond_signal(&cond);  
  65.         pthread_mutex_unlock(&mtx); //解锁  
  66.         sleep(1);  
  67.     }  
  68.     printf("thread 1 wanna end the line.So cancel thread 2./n");  
  69.     //关于pthread_cancel,有一点额外的说明,它是从外部终止子线程,子线程会在最近的取消点,退出  
  70.     //线程,而在我们的代码里,最近的取消点肯定就是pthread_cond_wait()了。  
  71.     pthread_cancel(tid);  
  72.     pthread_join(tid, NULL);  
  73.     printf("All done -- exiting/n");  
  74.     return 0;  
  75. }  

三、信号量(sem)

如同进程一样,线程也可以通过信号量来实现通信,虽然是轻量级的。信号量函数的名字都以"sem_"打头。线程使用的基本信号量函数有四个。

  1. 信号量初始化。
    int sem_init (sem_t *sem , int pshared, unsigned int value);
    这是对由sem指定的信号量进行初始化,设置好它的共享选项(linux 只支持为0,即表示它是当前进程的局部信号量),然后给它一个初始值VALUE。
  2. 等待信号量。给信号量减1,然后等待直到信号量的值大于0。
    int sem_wait(sem_t *sem);
  3. 释放信号量。信号量值加1。并通知其他等待线程。
    int sem_post(sem_t *sem);
  4. 销毁信号量。我们用完信号量后都它进行清理。归还占有的一切资源。
    int sem_destroy(sem_t *sem);
  1. #include <stdlib.h>  
  2. #include <stdio.h>  
  3. #include <unistd.h>  
  4. #include <pthread.h>  
  5. #include <semaphore.h>  
  6. #include <errno.h>  
  7. #define return_if_fail(p) if((p) == 0){printf ("[%s]:func error!/n", __func__);return;}  
  8. typedef struct _PrivInfo  
  9. {  
  10.     sem_t s1;  
  11.     sem_t s2;  
  12.     time_t end_time;  
  13. }PrivInfo;  
  14.   
  15. static void info_init (PrivInfo* thiz);  
  16. static void info_destroy (PrivInfo* thiz);  
  17. static void* pthread_func_1 (PrivInfo* thiz);  
  18. static void* pthread_func_2 (PrivInfo* thiz);  
  19.   
  20. int main (int argc, char** argv)  
  21. {  
  22.     pthread_t pt_1 = 0;  
  23.     pthread_t pt_2 = 0;  
  24.     int ret = 0;  
  25.     PrivInfo* thiz = NULL;  
  26.     thiz = (PrivInfo* )malloc (sizeof (PrivInfo));  
  27.     if (thiz == NULL)  
  28.     {  
  29.         printf ("[%s]: Failed to malloc priv./n");  
  30.         return -1;  
  31.     }  
  32.     info_init (thiz);  
  33.     ret = pthread_create (&pt_1, NULL, (void*)pthread_func_1, thiz);  
  34.     if (ret != 0)  
  35.     {  
  36.         perror ("pthread_1_create:");  
  37.     }  
  38.     ret = pthread_create (&pt_2, NULL, (void*)pthread_func_2, thiz);  
  39.     if (ret != 0)  
  40.     {  
  41.         perror ("pthread_2_create:");  
  42.     }  
  43.     pthread_join (pt_1, NULL);  
  44.     pthread_join (pt_2, NULL);  
  45.     info_destroy (thiz);  
  46.     return 0;  
  47. }  
  48. static void info_init (PrivInfo* thiz)  
  49. {  
  50.     return_if_fail (thiz != NULL);  
  51.     thiz->end_time = time(NULL) + 10;  
  52.     sem_init (&thiz->s1, 0, 1);  
  53.     sem_init (&thiz->s2, 0, 0);  
  54.     return;  
  55. }  
  56. static void info_destroy (PrivInfo* thiz)  
  57. {  
  58.     return_if_fail (thiz != NULL);  
  59.     sem_destroy (&thiz->s1);  
  60.     sem_destroy (&thiz->s2);  
  61.     free (thiz);  
  62.     thiz = NULL;  
  63.     return;  
  64. }  
  65. static void* pthread_func_1 (PrivInfo* thiz)  
  66. {  
  67.     return_if_fail(thiz != NULL);  
  68.     while (time(NULL) < thiz->end_time)  
  69.     {  
  70.         sem_wait (&thiz->s2);  
  71.         printf ("pthread1: pthread1 get the lock./n");  
  72.         sem_post (&thiz->s1);  
  73.         printf ("pthread1: pthread1 unlock/n");  
  74.         sleep (1);  
  75.     }  
  76.     return;  
  77. }  
  78. static void* pthread_func_2 (PrivInfo* thiz)  
  79. {  
  80.     return_if_fail (thiz != NULL);  
  81.     while (time (NULL) < thiz->end_time)  
  82.     {  
  83.         sem_wait (&thiz->s1);  
  84.         printf ("pthread2: pthread2 get the unlock./n");  
  85.         sem_post (&thiz->s2);  
  86.         printf ("pthread2: pthread2 unlock./n");  
  87.         sleep (1);  
  88.     }  
  89.     return;  

多线程同步

阅读数 413

Linux sem信号量线程同步机制四

博文 来自: zhaoxd200808501
没有更多推荐了,返回首页