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

多线程采用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_exitpthread_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线程同步机制

你可能感兴趣的:(多线程,pthread)