C语言-linux多线程编程

线程共享进程的资源(代码段、数据段、堆、核心段),线程也有自己的独立资源:TCB,线程id,寄存器,栈等;

线程分为用户级线程和内核级线程,每个用户级线程都绑定一个内核级线程;

 

一、多线程的基本函数

头文件:pthread.h

数据类型:pthread_t(typedef unsigned long int pthread_t;)

常用函数:

1. 获取线程自身ID。

pthread_t pthread_self(); 

2.判断两个线程ID是否相等,返回0 不相等,非零相等。
int pthread_equal(pthread_t threadid1,  pthread_t thread2) ;//线程名

3.新建线程
int pthread_create(pthread_t *restrict tidp,  const pthread_attr_t *restrict attr,  void* (*start_rtn) (void*),  void *restrict arg);//参数为线程名指针,属性指针,可为NULL,即为默认属性,线程运行函数指针,线程运行函数的参数指针。
  
4.线程终止 

主动退出:不释放资源

return 0;
void pthread_exit(void *rval_ptr);//线程自身主动退出

被动退出:

int pthread_cancel(pthread_t tid);//退出tid,不释放资源
int pthread_join(pthread_t tid, void **rval_ptr);//其他线程阻塞自身,等待tid退出,返回值存储在*rval_ptr,可用NULL,不保留tid的返回值,释放资源
  
5.线程清理
void pthread_cleanup_push(void(*rtn)(void*) , void *arg);
void pthread_cleanup_pop(intexecute);//intexecute非0,线程结束时会自动调用清理函数rtn;

6.线程属性//一般不需要专门设置

https://blog.csdn.net/zsf8701/article/details/7842392

数据类型:pthread_attr_t (typedef struct
{
    int                   etachstate;      //线程的分离状态,让子线程自动释放资源
    structsched_param     schedparam;      //线程的调度参数
    int                   inheritsched;    //线程的继承性
    int                   scope;           //线程的作用域
    size_t                guardsize;       //线程栈末尾的警戒缓冲区大小
    int                   stackaddr_set;   //线程的栈设置
    void*                 stackaddr;       //线程栈的位置
    size_t                stacksize;       //线程栈的大小
}pthread_attr_t;
 

线程属性的设置要通过函数get\set,先设置属性值,

然后再通过下面两个函数,使用该线程属性

int pthread_attr_init(pthread_attr_t* attr)

int pthread_attr_destroy(pthread_attr_t* attr)

 

 

二、线程的互斥(不能并发)

A、互斥锁(还有spinlock,自旋锁,与互斥锁的区别是:自旋锁阻塞时不会让出CPU,会忙等,即在线等,而互斥锁是让出CPU,离线等)

临界资源:共享资源

临界区:对共享资源进行操作的代码段

头文件:pthread.h

数据类型:pthread_mutex_t(一个结构体)

常用函数:

1.互斥锁的创建和销毁:

 int pthread_mutex_init(pthread_mutex_t * mutex, const pthread_mutexattr_t *  attr);//属性可初始化为NULL,即为默认

int pthread_mutex_destoryt(pthread_mutex_t * mutex);

2.互斥锁的上锁和解锁:

int pthread_mutex_lock(pthread_mutex_t *mutex) //锁已被占用时,阻塞

int pthread_mutex_trylock(pthread_mutex_t *mutex) //锁已被占用时,返回失败

int pthread_mutex_unlock(pthread_mutex_t *mutex) //解锁

3.互斥锁的属性设置//用到了再说

数据类型:pthread_mutexattr_t

设置函数:

int pthread_mutexattr_init(pthread_mutexattr_t* mutexattr)

int pthread_mutexattr_destroy(pthread_mutexattr_t* mutexattr)

互斥锁有四种类型,用到了再补充;

4.互斥锁的弊端:

缺乏多个读线程的并发性,为了解决这个问题,产生了读写锁。

B、读写锁

头文件:pthread.h

数据类型:pthread_rwlock_t(在使用时,报错了没有该类型,解决方法:编译时加上选项:-D_XOPEN_SOURCE=500,在.C文件里加#define _XOPEN_SOURCE 500这个不行)

常用函数:

1.读写锁的创建和销毁:

int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);//属性可为NULL,默认值
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

2.加锁和解锁:

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);//加读锁,第二次返回阻塞

int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);//加读锁,第二次返回失败

int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);//加写锁,第二次返回阻塞
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);//加写锁,第二次返回失败
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);//解锁

互斥情况:多次加读锁,不阻塞或失败;若读锁和写锁并发,返回阻塞或失败

 

 

三、线程的同步(在互斥的基础上,安排好线程的先后依赖关系)

C、条件变量(实质是使用一个等待队列和判断条件,该等待队列和判断条件都是共享资源,需要用互斥锁保护,故条件变量需要与互斥锁配合使用,个人觉得这个不是很好用,不太好理解)

头文件:pthread.h

数据类型:pthread_cond_t

常见函数:

1.条件变量的创建和销毁:

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

2.条件变量的等待:

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

3.条件变量的通知:

int pthread_cond_signal(pthread_cond_t *restrict cond);

D、线程信号量(实质是非负整数计数器,是共享资源的数目;当信号量小于0时,阻塞或失败)

头文件:semaphore.h

数据类型:sem_t

常用函数:

1.信号量的创建和销毁:

int sem_init(sem_t *sem, int pshared, unsigned int value);//int pshared为共享属性,一般为0;unsigned int value,信号量的初始值

int sem_destroy(sem_t *sem);

2.信号量加减操作:

int sem_wait(sem_t *sem);//信号量减1,小于0时阻塞

int sem_trywait(sem_t *sem);//信号量减1,小于0时失败

int sem_post(sem_t *sem);//信号量加1

 

四、死锁

产生死锁的条件:

(1)互斥条件:指进程/线程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程/线程占用。如果此时还有其它进程/线程请求该资源,则请求者只能等待,直至占有该资源的进程/线程用毕释放。 
(2)请求和保持条件:指进程/线程已经保持了至少一个资源,但又提出了新的资源请求,而该资源又被其它进程/线程占有,此时请求进程/线程阻塞,但又对自己获得的其它资源保持不放。 
(3)不剥夺条件:指进程/线程已获得资源,在使用完之前,不能被剥夺,只能在使用完时由自己释放。 
(4)环路等待条件:指在发生死锁时,必然存在一个进程/线程等待资源的环形链,即进程/线程集合(P0,P1,P2,…,Pn)中的P0正在等待一个P1占用的资源;P1正在等待一个P2占用的资源,……,Pn正在等待已被P0占用的资源

避免死锁的方法:

1.一次性请求所有临界资源;

2.使用trylock代替lock;

3.各互斥线程采用相同的次序访问共享资源

 

五、实例

1、互斥锁使用:在一个子线程里写,另一个子线程里读,只保证了子线程是互斥的,并没有实现写与读之间的同步

#include 
#include 
#include 
#include 

typedef struct Data
{
    int sum;
    pthread_mutex_t mlock;
}data;

void* fn_0(void* arg)
{
    data* da1 = (data*)arg;
    for (int i = 0; i<10; i++)
    {
        pthread_mutex_lock(&da1->mlock);
        da1->sum += 1;
        printf("\nwrite data:%d", da1->sum);
        pthread_mutex_unlock(&da1->mlock);
    }
    return (void*)da1;
}
void* fn_1(void* arg)
{
    data* da2 = (data*)arg;
    for (int i = 0; i<10; i++)
    {
        pthread_mutex_lock(&da2->mlock);
        printf("\nread data:%d", da2->sum);
        pthread_mutex_unlock(&da2->mlock);
    }
    return (void*)da2;
}

int main()
{
    pthread_t thread0;
    pthread_t thread1;
    data da;
    da.sum = 0;
    pthread_mutex_init(&da.mlock, NULL);
    pthread_create(&thread0, NULL, fn_0, (void*)&da);
    pthread_create(&thread1, NULL, fn_1, (void*)&da);

    pthread_join(thread0, NULL);
    pthread_join(thread1, NULL);

    pthread_mutex_destroy(&da.mlock);

    return 0;
}

2、信号量使用:在一个子线程里写,另一个子线程里读,实现写与读之间的同步

#include 
#include 
#include 
#include 
#include 

int sum;
sem_t sem1;
sem_t sem2;

void* fn_0(void* arg)
{
    int* sum = (int*)arg;
    for (int i = 0; i<10; i++)
    {
        
        *sum += 1;
        printf("write data:%d\n", *sum);
        sem_post(&sem2);
        sem_wait(&sem1);
    }

    return (void*)arg;
}
void* fn_1(void* arg)
{
    int* sum = (int*)arg;
    for (int i = 0; i<10; i++)
    {
        sem_wait(&sem2);
        printf("read data:%d\n", *sum);
        sem_post(&sem1);
    
    }
    return (void*)arg;
}

int main()
{
    sum=0;
    pthread_t thread0;
    pthread_t thread1;
    sem_init(&sem1,0,0);
    sem_init(&sem2,0,0);
    pthread_create(&thread0, NULL, fn_0, (void*)&sum);
    pthread_create(&thread1, NULL, fn_1, (void*)&sum);

    pthread_join(thread0, NULL);
    pthread_join(thread1, NULL);

    sem_destroy(&sem1);
    sem_destroy(&sem2);

    return 0;
}

3.读写锁的使用:

#include 
#include 
#include 
#include 
#include 

typedef struct Data
{
    int sum;
    int rd_count;
    int wr_count;
    pthread_rwlock_t rwlock;
}data;

time_t start = 0;
time_t end = 0;

void* fn_0(void* arg)
{
    data* da1 = (data*)arg;
    int read_sum;
    start = time(NULL);
    while(1)
    {
        end = time(NULL);
        if (end == start + 180)
                break;
        pthread_rwlock_rdlock(&da1->rwlock);
        read_sum = da1->sum;
        da1->rd_count +=1;
        pthread_rwlock_unlock(&da1->rwlock);
    }
    return (void*)da1;
}
void* fn_1(void* arg)
{
    data* da2 = (data*)arg;
    while (1)
    {
        end = time(NULL);
        if (end == start + 180)
                break;
        pthread_rwlock_wrlock(&da2->rwlock);
        da2->wr_count +=1;
        da2->sum += 1;
        pthread_rwlock_unlock(&da2->rwlock);

    }
    
    return (void*)da2;
}

int main()
{
    pthread_t thread0;
    pthread_t thread1;
    data da;
    da.sum = 0;
    da.rd_count = 0;
    da.wr_count = 0;
    pthread_rwlock_init(&da.rwlock, NULL);
    pthread_create(&thread0, NULL, fn_0, (void*)&da);
    pthread_create(&thread1, NULL, fn_1, (void*)&da);

    pthread_join(thread0, NULL);
    pthread_join(thread1, NULL);

    printf("rd_count,wr_count:%d,%d\n",da.rd_count,da.wr_count);

    pthread_rwlock_destroy(&da.rwlock);

    return 0;
}

 

你可能感兴趣的:(linux,c)