LLINUX_C编程实战-第八章《线程管理》学习笔记

  1. 线程的概念
    一个进程可以有许多线程;进程是线程的容器;线程的执行时并发的;同一进程的不同线程共享进程的数据段、代码段、堆区;但每个线程都有自己的栈段(从进程的栈区分出来的)、线程ID、寄存器等等;
    使用return 或 exit退出了主进程,则其含有的线程也结束了;可以用pthread_exit退出主线程 或 用pthread_join函数等待相应线程的结束
  2. 线程的创建
    int pthread_create(pthread_t id, pthread_attr_t *attr, (void ) function, void *arg ); 入参分别为 线程id, 线程属性(一般为NULL), 线程函数指针,传给线程的参数;
    线程的退出:pthread_exit( void * exit_code);
    线程的阻塞等待:pthread_join(id,void ** exit_code);

    PS:   同步:在访问资源的时候,以某种特定顺序的方式去访问资源(你先访问还是我先访问) 
            互斥:一个资源每次只能被一个进程所访问
            同步与互斥是保证在高效率运行的同时,可以正确运行。大部分情况下**同步是在互斥的基础上进行的**。
    

    线程的同步方式有三种:互斥锁、条件变量、信号量。
    三种实质上都是一种锁机制,通过创建全局变量(放在数据区,线程可以共享),调用来实现线程的同步与互斥
    ps: IPC的消息队列、信号量、共享内存也可以看做是一种锁机制,但其通过ftok函数创建key键值(可以放在栈区),进程之间通过键值彼此关联起来,相互配合实现进程的通信(数据传输,同步与互斥) 。
    一、互斥锁(mutex)
    通过锁的机制实现线程间的同步。
    1.初始化锁:Linux系统下,锁的数据类型为pthread_mutex_t,初始化分为两种方式。
    静态分配: pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    动态分配: int pthread_mutex_init(pthread_mutex *mutex, const pthread_mutex_attr_t *mutexattr);
    2.加锁:对共享资源的访问,需对互斥量加锁;如果互斥量已经上锁,调用进程会堵塞,直到被解锁。
    int pthread_mutex_lock(pthread_mutex_t *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_t *mutex) ;
    调用成功返回值为0;否则为非0的错误码;
    参照网上的代码,对代码段进行加锁,如下:

**#include <stdio.h>
#include <pthread.h>
#include <unistd.h>**
pthread_mutex_t mutex; //互斥锁

// 打印机
void printer(char *str)
{
    pthread_mutex_lock(&mutex); //上锁
    while(*str!='\0')
    {
        putchar(*str);  
        fflush(stdout);
        str++;
        sleep(1);
    }
    printf("\n"); 
    pthread_mutex_unlock(&mutex); //解锁
}

// 线程一
void *thread_fun_1(void *arg)
{
    char *str = "hello";
    printer(str); //打印
}

// 线程二
void *thread_fun_2(void *arg)
{
    char *str = "world";
    printer(str); //打印
}

int main(void)
{
    pthread_t tid1, tid2;

    pthread_mutex_init(&mutex, NULL); //初始化互斥锁

    // 创建 2 个线程
    pthread_create(&tid1, NULL, thread_fun_1, NULL);
    pthread_create(&tid2, NULL, thread_fun_2, NULL);

    // 等待线程结束,回收其资源
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL); 

    pthread_mutex_destroy(&mutex); //销毁互斥锁

    return 0;
}

二、条件变量(cond):
和互斥锁不同的是条件变量是用来等待的,不是用来上锁的,一般情况下和锁共用;在等待条件变量的过程中,pthread_cond_wait(&cond,&mutex)会阻塞线程,并释放互斥锁;其它线程在完成功能,调用pthread_cond_signal(&cond),会激活调用条件变量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);

三、信号量(sem)
线程用的信号量和进程间通信用的信号量概念是一样的;只有0和1数值得为二进制信号量,只能进行原子操作,主要用于保护一段代码在一段时间内只允许单个线程访问,一般以sem_函数开头,定义在semaphore.h头文件中;数据类型为sem_t;
1.信号量的初始化
int sem_init (sem_t *sem , int pshared, unsigned int value); 其中pshared为它的共享选项(linux 只支持为0,即表示它是当前进程的局部信号量),然后给它一个初始值VALUE(0 or 1)。
2.等待信号量
int sem_wait(sem_t *sem);等待并阻塞线程,直到sem信号量的值大于0,然后将值-1,执行下一步程序;
3.释放信号量
int sem_post(sem_t *sem);给sem值+1,并通知其它因等待而阻塞的线程可以运行啦;
4.销毁信号量
int sem_destroy(sem_t *sem). 信号量也是一种资源(可以理解为一种存放在数据区的全局变量),用完后需清理,归返占有的资源。

**#include <semaphore.h>**
#include <errno.h>
#define return_if_fail(p) if((p) == 0){printf ("[%s]:func error!/n", __func__);return;}
typedef struct _PrivInfo
{
    sem_t s1;
    sem_t s2;
    time_t end_time;
}PrivInfo;

static void info_init (PrivInfo* thiz);
static void info_destroy (PrivInfo* thiz);
static void* pthread_func_1 (PrivInfo* thiz);
static void* pthread_func_2 (PrivInfo* thiz);

int main (int argc, char** argv)
{
    pthread_t pt_1 = 0;
    pthread_t pt_2 = 0;
    int ret = 0;
    PrivInfo* thiz = NULL;
    thiz = (PrivInfo* )malloc (sizeof (PrivInfo));
    if (thiz == NULL)
    {
        printf ("[%s]: Failed to malloc priv./n");
        return -1;
    }
    info_init (thiz);
    ret = pthread_create (&pt_1, NULL, (void*)pthread_func_1, thiz);
    if (ret != 0)
    {
        perror ("pthread_1_create:");
    }
    ret = pthread_create (&pt_2, NULL, (void*)pthread_func_2, thiz);
    if (ret != 0)
    {
        perror ("pthread_2_create:");
    }
    pthread_join (pt_1, NULL);
    pthread_join (pt_2, NULL);
    info_destroy (thiz);
    return 0;
}
static void info_init (PrivInfo* thiz)
{
    return_if_fail (thiz != NULL);
    thiz->end_time = time(NULL) + 10;
    sem_init (&thiz->s1, 0, 1);
    sem_init (&thiz->s2, 0, 0);
    return;
}
static void info_destroy (PrivInfo* thiz)
{
    return_if_fail (thiz != NULL);
    sem_destroy (&thiz->s1);
    sem_destroy (&thiz->s2);
    free (thiz);
    thiz = NULL;
    return;
}
static void* pthread_func_1 (PrivInfo* thiz)
{
    return_if_fail(thiz != NULL);
    while (time(NULL) < thiz->end_time)
    {
        sem_wait (&thiz->s2);
        printf ("pthread1: pthread1 get the lock./n");
        sem_post (&thiz->s1);
        printf ("pthread1: pthread1 unlock/n");
        sleep (1);
    }
    return;
}
static void* pthread_func_2 (PrivInfo* thiz)
{
    return_if_fail (thiz != NULL);
    while (time (NULL) < thiz->end_time)
    {
        sem_wait (&thiz->s1);
        printf ("pthread2: pthread2 get the unlock./n");
        sem_post (&thiz->s2);
        printf ("pthread2: pthread2 unlock./n");
        sleep (1);
    }
    return;
}

猜你喜欢

转载自blog.csdn.net/caozhigang129/article/details/78111231