您的位置:首页 > 编程语言 > C语言/C++

C++服务器(五):pthread多线程编程

2016-03-19 20:24 525 查看
多线程采用pthread库。

考虑到多平台下的实现并不会很容易,还有多线程间的同步等问题,采用一个比较通用的库就好了,这样减少很多工作(其实是我不会使用别的库)

创建一个线程

函数原型:

#include <pthread.h>

int pthread_create(pthread_t  * tidp,const pthread_attr_t * attr,void *  (* start_rtn)(void * ),void * args);


pthread_create

这是POSIX线程库的内容,所以编译的时候要加上:
-lpthread


示例代码:

#include<pthread.h>
#include<iostream>
using namespace std;
void* say(void*)
{
cout<<"hello.."<<endl;
}

void threadTest()
{
const int THREAD_NUM = 5;
pthread_t tids[THREAD_NUM]; //线程的id
for(int i=0;i<THREAD_NUM;++i)
{
int result = pthread_create(tids+i, nullptr, say, nullptr);
if(result != 0)
{
cout<<"pthread_create error : "<<result<<endl;

}
}
pthread_exit(nullptr);//没有这句的话,当此线程over 的时候,其他线程也over
}
int main()
{
threadTest();
}


代码倒是很简单易懂,配合函数说明,看一下就能明白了。

我想说的是,关于
pthread_exit
pthread_join
的区别;

pthread_join

pthread_exit

前者用于退出当前线程,但是其子线程仍然继续运行

后者是一个线程同步函数,一般主线程使用,它会等待join 进去的所有线程结束之后才会执行后续的代码。

看函数原型就可以知道,join 是可以捕获exit 的返回值的。

如果最后没有exit 也没有 join 的话, 那么当线程结束的时候,其子线程也会结束,真是太可怕了。

设置线程的属性

pthread_create 的第二个参数是用来设置线程的属性的,在这之前需要先初始化一下。

结构体是:
pthread_attr_init


参考:pthread_create()之前的属性设置

线程同步

互斥锁

函数:

pthread_mutex_t          //--互斥锁(变量)
int pthread_mutex_lock(pthread_mutex_t *mutex);         //--加锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);        //--解锁
int pthread_mutex_destroy(pthread_mutex_t *mutex); //mutex 指向要销毁的互斥锁的指针


code:

pthread_mutex_t mutex; //互斥锁
int sum = 0;

void* say(void*)
{
pthread_mutex_lock(&mutex);  //加锁
sum = sum+2;
cout<<sum<<endl;
pthread_mutex_unlock(&mutex);  //解锁
}


嗯,就是这么简单~

条件变量

函数

pthread_cond_t        //变量声明
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)  //等待信号,同时释放互斥锁
int pthread_cond_wait(pthread_cond_t *cond)//释放信号
int pthread_cond_destroy(pthread_cond_t *cond);//销毁条件变量锁


为了避免死锁,条件变量应该和互斥变量一起使用。

pthread_cond_wait
应该在互斥区编写,
pthread_cond_signal
可以在互斥区,也可以在互斥区外。

pthread_cond_wait
的时候,会进入阻塞状态,等待唤醒,同时也会释放互斥锁。只有同时当条件变量被唤醒和得到互斥锁的时候,才会重新运行。

pthread_cond_signal
的时候,会唤醒另一个线程,但是此时并不会释放互斥锁。

code:

pthread_mutex_t mutex; //互斥锁
pthread_cond_t cond; //条件变量
int sum = 0; //当sum 为奇数时 job1 动, 否则job2动

void* job1(void*)
{
while(sum<=5)
{
pthread_mutex_lock(&mutex);
if((sum&0x1) == 0)
{
pthread_cond_wait(&cond, &mutex); //等待job2 来叫叫我,同时释放mutex锁
}
cout<<"i am job1,now sum  = "<<sum<<endl;
++sum;
pthread_mutex_unlock(&mutex);
}
cout<<"job1 is over"<<endl;

}

void* job2(void*)
{
while(sum<=5)
{
pthread_mutex_lock(&mutex);
if((sum&0x1)==1)
{
pthread_cond_signal(&cond);//唤醒job1 ,但是mutex锁还没有解放
cout<<"i am job2 ,now is your turn--job1"<<endl;
}
else
{
cout<<"i am job2,now sum  = "<<sum<<endl;
++sum;
}
pthread_mutex_unlock(&mutex);
sleep(1);
}
cout<<"job2 is over"<<endl;
pthread_cond_signal(&cond);
}


输出:

i am job2,now sum  = 0
i am job2 ,now is your turn--job1
i am job1,now sum  = 1
i am job2,now sum  = 2
i am job2 ,now is your turn--job1
i am job1,now sum  = 3
i am job2,now sum  = 4
i am job2 ,now is your turn--job1
i am job1,now sum  = 5
job1 is over
job2 is over


读写锁

读写锁与mutex类似,不过读写锁允许更高的并行性。mutex只有两种状态(lock & unlock),而读写锁有三种状态读模式下加锁(所有以读模式对它进行加锁的线程都可以得到访问权,写模式访问则会被阻塞),写模式加锁(所有试图访问的都会被阻塞)和无锁。

函数

pthread_rwlock_t //变量
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);
/* 如果希望读写锁有默认的属性,则分配一个NULL给attr */
int pthread_rwlock_destory(pthread_rwlock_t *rwlock);
int pthread_rwlock_rdlock(pthread_rwlock_t  *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t  *rwlock);
int pthread_rwlock_tryrdlock(pthread_rwlock_t  *rwlock);
int pthread_rwlock_trywrlock(pthread_rwlock_t  *rwlock);
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);


code:

//读写者模型
pthread_rwlock_t job;

void* wantToRead(void* n)
{
while(true)
{
cout<<"  I want to read"<<endl;
pthread_rwlock_rdlock(&job);
cout<<"  I am reading ,and sum = "<<sum<<endl;
pthread_rwlock_unlock(&job);
sleep(1);
}
}

void* wantToWrite(void*)
{
while(true)
{
cout<<" I want to write"<<endl;
pthread_rwlock_wrlock(&job);
++sum;
cout<<"end--"<<endl;
pthread_rwlock_unlock(&job);
sleep(4);
}
}

void threadTest()
{
const int THREAD_NUM = 6;
pthread_t tids[THREAD_NUM]; //线程的id
pthread_rwlock_init(&job, nullptr);
int i=0;
for(;i<THREAD_NUM; ++i)
{
if(i>=THREAD_NUM-2)
pthread_create(tids+i, nullptr, wantToWrite, nullptr);
else
pthread_create(tids+i, nullptr, wantToRead, nullptr);
}
pthread_exit(nullptr);
}


输出:

I want to read
I am reading ,and sum =   I want to read0
I want to read
I am reading ,and sum = 0
I want to write
end--

I am reading ,and sum = 1
I want to write  I want to read
I am reading ,and sum = 1

end--
I want to read
I am reading ,and sum = 2
I want to read
I am reading ,and sum = 2
I want to read
I am reading ,and sum =   I want to read
I am reading ,and sum = 22


之所以会这样是因为read的话,是可以允许多人同时read的,但是write则只能 one by one。

信号量

信号量并不是pthread 的(貌似是这样)

函数

#include<semaphore.h>
sem_t //变量声明
int sem_init(sem_t *sem, int pshared, unsigned int value);//pshared不为0时此信号量在进程间共享,否则只能为当前进程的所有线程共享;value给出了信号量的初始值

int sem_wait(sem_t * sem);//给信号量减-1,down 操作,如果信号量 < 0 则阻塞。
int sem_post(sem_t * sem);//给信号量加-1,up 操作,如果信号量 >= 0 则唤醒别的进程


还有其他函数,这里不介绍,移步参考资料。

可以用这个实现生产者消费者模型

//生产者消费者模型
sem_t semMutex; //1
sem_t empty;//n=3
sem_t full;//0
void* producer(void* n)
{
int i = *(reinterpret_cast<int*>(n));
while(true)
{
sem_wait(&empty);
sem_wait(&semMutex);
++sum;
cout<<"producer"<<i<<" is create one, sum = "<<sum<<endl;
sem_post(&semMutex);
sem_post(&full);
sleep(1);
}
}
void* consumer(void* n)
{
int num = *(reinterpret_cast<int*>(n));
while(true)
{
sem_wait(&full);
sem_wait(&semMutex);
--sum;
cout<<"consumer"<<num<<" is serviced one, sum = "<<sum<<endl;
sem_post(&semMutex);
sem_post(&empty);
sleep(1);
}
}


参考资料:

c++多线程编程

pthread_create()之前的属性设置

线程--线程基本操作

条件变量pthread_cond_t怎么用

Linux信号量 sem_t简介

(其实是讲信号量的)Linux多线程同步的几种方式

(读写者锁)pthread线程同步机制
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: