Linux 15 多线程(一)创建终止和同步

进程与线程

线程拥有自己的栈(因为线程有自己的局部变量),但与它的创建者共享全局变量、文件描述符、信号句柄和当前目录状态。
通过fork创建子进程,创建出该进程的一份拷贝,这个新进程拥有自己的变量和自己的PID,它的时间调度是独立的,拥有独立的地址空间,它的执行几乎完全独立于父进程。
进程可以看成一个资源的基本单位,而线程是程序调度的基本单位,一个进程内部的线程之间共享进程获得的时间片。

_REENTRANT宏

编写的多线程程序,通过定义宏_REENTRANT来告诉编译器我们需要可重入功能,这个宏的定义必须出现于程序中的任何#include语句之前。
_REENTRANT为我们做三件事情,并且做的非常优雅:
(1)它会对部分函数重新定义它们的可安全重入的版本,这些函数名字一般不会发生改变,只是会在函数名后面添加_r字符串,如函数名gethostbyname变成gethostbyname_r。
(2)stdio.h中原来以宏的形式实现的一些函数将变成可安全重入函数。
(3)在error.h中定义的变量error现在将成为一个函数调用,它能够以一种安全的多线程方式来获取真正的errno的值。

线程的基本函数

1线程创建
#include <pthread.h>
int pthread_create(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);

参数说明:
thread:指向pthread_create类型的指针,用于引用新创建的线程。
attr:用于设置线程的属性,一般不需要特殊的属性,所以可以简单地设置为NULL。
*(*start_routine)(void *):传递新线程所要执行的函数地址。
arg:新线程所要执行的函数的参数。
调用如果成功,则返回值是0,如果失败则返回错误代码。

2线程终止
#include <pthread.h>
void pthread_exit(void *retval);

参数说明:
retval:返回指针,指向线程向要返回的某个对象。
线程通过调用pthread_exit函数终止执行,并返回一个指向某对象的指针。注意:绝不能用它返回一个指向局部变量的指针,因为线程调用该函数后,这个局部变量就不存在了,这将引起严重的程序漏洞。

3线程同步
#include <pthread.h>
int pthread_join(pthread_t th, void **thread_return);

参数说明:
th:将要等待的线程,线程通过pthread_create返回的标识符来指定。
thread_return:一个指针,指向另一个指针,而后者指向线程的返回值。

//thread1.c
/* 线程创建、同步和删除 */

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

void *thread_function(void *arg);

char message[] = "hello world!";

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

    /* 创建线程 */
    res = pthread_create(&a_thread, NULL, thread_function, (void *)message);
    if (0 != res)
    {
        perror("Thread create failed!");
        exit(EXIT_FAILURE);
    }

    printf("Waiting for thread to finish...\r\n");

    /* 线程同步,当前线程等待a_thread结束 */
    res = pthread_join(a_thread, &thread_result);

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

    exit(EXIT_FAILURE); 
}

/* 新线程的回调函数 */
void *thread_function(void *arg)  
{  
    printf("thread_function is running. Argument is %s\r\n", (char *)arg);  
    sleep(3);

    /* 修改全局变量,这里也可以看出线程之间共享全局变量 */
    strcpy(message, "Bye!");

    /* 线程终止,返回某个对象的指针,不能是局部变量 */
    pthread_exit("Thank you for your CPU time!");  
} 


4线程同时执行

利用一个原理:即除了局部变量外,所有其他的变量在一个进程中的所有线程之间是共享的。
在这个程序中,我们是在两个线程之间使用轮询技术,这种方式称为忙等待,所以它的效率会很低。

//thread2.c
/* 线程同步执行,通过对一个全局变量的轮询控制 */

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

void *thread_function(void *arg);

int flag = 1;

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

    /* 创建线程 */
    res = pthread_create(&a_thread, NULL, thread_function, NULL);
    if (0 != res)
    {
        perror("Thread create failed!");
        exit(EXIT_FAILURE);
    }

    while (count++ <= 20)  
    {  
        if (flag == 1)  
        {  
            printf ("1\r\n");  
            flag = 2;  
        }  
        else  
        {  
            sleep(1);  
        }  
    }
    
    printf("Waiting for thread to finish...\r\n");

    /* 线程同步,当前线程等待a_thread结束 */
    res = pthread_join(a_thread, &thread_result);

    printf("Thread joined, it returned %s\r\n", (char *)thread_result);

    exit(EXIT_FAILURE); 
}

/* 新线程的回调函数 */
void *thread_function(void *arg)  
{  
    int count = 1;  
  
    while (count++ <= 20)  
    {  
        if (flag == 2)  
        {  
            printf("2\r\n");  
            flag = 1;  
        }  
        else  
        {  
            sleep(1);  
        }  
    } 

    /* 线程终止,返回某个对象的指针,不能是局部变量 */
    pthread_exit("Thank you for your CPU time!");  
} 

5线程同步

5.1信号量同步
1.信号量创建

#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);

参数说明:
sem:信号量对象。
pshared:控制信号量的类型,0表示这个信号量是当前进程的局部信号量,否则,这个信号量就可以在多个进程之间共享。
value:信号量的初始值。

2.信号量控制

#include <semaphore.h>
int sem_wait(sem_t *sem);
int sem_post(sem_t *sem);

sem_post的作用是以"原子操作"的方式给信号量的值加1。
sem_wait的作用是以"原子操作"的方式给信号量的值减1,但它会等到信号量非0时才会开始减法操作。
如果对值为0的信号量调用sem_wait,这个函数就会等待,直到有线程增加了该信号量的值使其不再为0。

3.信号量销毁

#include <semaphore.h>
int sem_destory(sem_t *sem);

这个函数的作用是,用完信号量后对它进行清理,清理该信号量所拥有的资源。如果你试图清理的信号量正被一些线程等待,就会收到一个错误。
与大多数Linux函数一样,这些函数在成功时都返回0。

//thread3.c
/* 通过信号量sem_t实现同步 */

#include <pthread.h>  
#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <semaphore.h>  
  
#define SIZE 1024  
  
void *thread_function(void *arg);  
  
char buffer[SIZE];  
sem_t sem;  
  
int main()  
{  
    int res;  
    pthread_t a_thread;  
    void *thread_result;  

    /* 初始化信号量 */
    res = sem_init(&sem, 0, 0);  
    if (res != 0)  
    {  
        perror("Sem init failed");  
        exit(EXIT_FAILURE);  
    }  
  
    res = pthread_create(&a_thread, NULL, thread_function, NULL);  
    if (res != 0)  
    {  
        perror("Thread create failed");  
        exit(EXIT_FAILURE);  
    }  
  
    printf("Input some text. Enter 'end' to finish\r\n");  
  
    while (scanf("%s", buffer))  
    {
        /* 释放信号量 */
        sem_post(&sem);  
        if (strncmp("end", buffer, 3) == 0)  
            break;  
    }  
  
    printf ("Waiting for thread to finish...\r\n");  
  
    res = pthread_join(a_thread, &thread_result);  
    if (res != 0)  
    {  
        perror("Thread join failed");  
        exit(EXIT_FAILURE);  
    }  
  
    printf ("Thread join\r\n");  

    /* 销毁信号量 */
    sem_destroy(&sem);  
  
    exit(EXIT_SUCCESS);  
}  
  
void *thread_function(void *arg)  
{  
    sem_wait(&sem);  
    while (strncmp("end", buffer, 3) != 0)  
    {  
        printf("You input %d characters\r\n", strlen(buffer)); 
        /* 等待信号量,跳回主线程 */
        sem_wait(&sem);
    }

    sleep(2);
    
    printf ("thread finished\r\n");
    pthread_exit(NULL);  
}


5.2互斥锁实现同步、互斥
另一种用在多线程程序中同步访问的方法是使用互斥量。它允许程序员锁住某个对象,使得每次只能有一个线程访问它。为了控制对关键代码的访问,必须在进入这段代码之前锁住一个互斥量,然后在完成操作之后解锁它。
用于互斥量的基本函数和用于信号量的函数非常相似:

#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t, *mutexattr);
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_mutex_destory(pthread_mutex_t *mutex);

与其他函数一样,成功时返回0,失败时将返回错误代码,但这些函数并不设置errno,所以必须对函数的返回代码进行检查。互斥量的属性设置这里不讨论,因此设置成NULL。

//thread4.c
/* 通过pthread_mutex_t实现互斥 */

#include <pthread.h>  
#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <semaphore.h>  
  
#define SIZE 1024  
char buffer[SIZE];

void *thread_function(void *arg);  
pthread_mutex_t mutex;

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

    /* 初始化信号量 */
    res = pthread_mutex_init(&mutex, NULL);  
    if (res != 0)  
    {  
        perror("Mutex init failed");  
        exit(EXIT_FAILURE);
    }  
  
    res = pthread_create(&a_thread, NULL, thread_function, NULL);  
    if (res != 0)  
    {  
        perror("Thread create failed");  
        exit(EXIT_FAILURE);  
    }  
  
    printf("Input some text. Enter 'end' to finish\r\n");  
  
    while (1)  
    {
        /* 操作前全局变量必须上锁 */
        pthread_mutex_lock(&mutex);  
        scanf("%s", buffer);
        /* 操作完解锁 */
        pthread_mutex_unlock(&mutex);  
        if (strncmp("end", buffer, 3) == 0)  
            break;  
        sleep(1);  
    }

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

    /* 销毁信号量 */
    pthread_mutex_destroy(&mutex);  
  
    exit(EXIT_SUCCESS);  
}  
  
void *thread_function(void *arg)  
{  
    sleep(1);
    
    while (1)  
    {  
        /* 操作前全局变量必须上锁 */
        pthread_mutex_lock(&mutex);  
        printf("You input %d characters\r\n", strlen(buffer));
        /* 操作完解锁 */
        pthread_mutex_unlock(&mutex);  
        if (strncmp("end", buffer, 3) == 0)  
            break;  
        sleep(1);  
    }

    sleep(2);
    printf ("thread finished\r\n");
    pthread_exit(NULL);
}


6多线程

pthread_create创建多个线程:

//thread5.c
/* 基本多线程创建 */

#include <pthread.h>  
#include <stdio.h>  
#include <stdlib.h>  
  
#define NUM 8  
  
void *thread_function(void *arg);  
  
int main()  
{  
    int res;  
    pthread_t a_thread[NUM];  
    void *thread_result;  
    int index;  
  
    for (index = 0; index < NUM; ++index) {  
        res = pthread_create(&a_thread[index], NULL, thread_function, (void *)index);  
        if (res != 0)  
        {  
            perror("Thread create failed!");  
            exit(EXIT_FAILURE);  
        }  
        sleep(1);  
    }  
  
    printf("Waiting for threads to finished...\r\n");  
  
    for (index = NUM - 1; index >= 0; --index)  
    {  
        res = pthread_join(a_thread[index], &thread_result);  
        if (res == 0)  
        {  
            printf("Picked up a thread:%d\r\n", index + 1);  
        }  
        else  
        {  
            perror("pthread_join failed\r\n");  
        }  
    }  
  
    printf("All thread done\r\n");  
      
    exit(EXIT_SUCCESS);  
}  
  
void *thread_function(void *arg)  
{  
    int my_number = (int)arg;  
    int rand_num;  
  
    printf("thread_function is running. Argument was %d\r\n", my_number);  
    rand_num = 1 + (int)(9.0 * rand()/(RAND_MAX + 1.0));  
    sleep(rand_num);  
    printf("Bye from %d\r\n", my_number);  
    pthread_exit(NULL);  
}  

Makefile:

all:thread1 thread2 thread3 thread4 thread5 thread6 thread7 thread8

thread1: thread1.c
	gcc -D_REENTRANT thread1.c -o thread1 -lpthread

thread2: thread2.c
	gcc -D_REENTRANT thread2.c -o thread2 -lpthread

thread3: thread3.c
	gcc -D_REENTRANT thread3.c -o thread3 -lpthread

thread4: thread4.c
	gcc -D_REENTRANT thread4.c -o thread4 -lpthread

thread5: thread5.c
	gcc -D_REENTRANT thread5.c -o thread5 -lpthread

thread6: thread6.c
	gcc -D_REENTRANT thread6.c -o thread6 -lpthread

thread7: thread7.c
	gcc -D_REENTRANT thread7.c -o thread7 -lpthread

thread8: thread8.c
	gcc -D_REENTRANT thread8.c -o thread8 -lpthread
clean: 
	rm -f thread1 *.o thread2 thread3 thread4 thread5 thread6 thread7 thread8

【参考链接:http://blog.csdn.net/monkey_d_meng/article/details/5628663】

猜你喜欢

转载自blog.csdn.net/zzj244392657/article/details/92588924