【linux高级程序设计】(第十二章)Linux多线程编程

线程与进程对比

1.用户空间对比

2.内核空间资源对比

在创建线程时,Linux内核仍然创建一个新的PCB来标识这个线程。内核并不认为进程与线程有差别。

进程是操作系统管理资源的基本单元,线程时Linux系统调度的基本单元。

3.进程线程函数对比

创建线程

int pthread_create (pthread_t *__restrict __newthread,

          __const pathread_attr_t *__restrict __attr,

          void *(*__start_routine) (void *),

                              void *__restrict __arg)

成功返回0,失败返回非0值。

#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/syscall.h>
struct message
{
    int i;
    int j;
};

void * hello(struct message *str)
{
    printf("child, the tid = %lu, pid = %ld
", pthread_self(),syscall(SYS_gettid));
    printf("the arg.i is %d, arg.j is %d
", str->i, str->j);
    while(1);
}

int main(int argc, char *argv[])
{
    struct message test;
    pthread_t thread_id;
    test.i = 10;
    test.j = 20;
    pthread_create(&thread_id, NULL, (void *)*hello, &test); //创建线程
    printf("parent, the tid = %lu, pid = %ld
", pthread_self(),syscall(SYS_gettid));
    pthread_join(thread_id, NULL);
}

注意,编译的时候需要加上 -lpthread 

gcc -o 名字 源文件 -lphtread  (无法使用perror打印错误信息,因为不修改系统全局变量errno)

void pthread_exit (void *__retval) :线程退出,与exit()函数类似

int pthread_join (pthread_t __th, void **__thread_return) :阻塞调用当前线程的线程,直到此线程退出。类似于wait函数。

第一个参数:被等待线程ID。必须等待关联线程

第二个参数:用户定义指针,存储被等待线程返回值。

int pthread_detach (pthread_t __th) :设置线程为独立线程。成功返回0.

测试线程退出时全局变量和堆变量:仍然可用

#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void *helloworld(char *argc);
int main(int argc, char *argv[])
{
    int error;
    int *temptr;
    pthread_t thread_id;
    pthread_create(&thread_id, NULL, (void *)*helloworld, "helloworld");
    //测试下面两者是否有区别
    printf("*p = %x, p = %x
", *helloworld, helloworld);
    if(error = pthread_join(thread_id, (void**)&temptr))
    {
        perror("pthread_join");
        exit(EXIT_FAILURE);
    }
    //打印子线程退出时的值
    printf("temp = %x, *temp = %c
", temptr, *temptr);
    //修改堆空间 测试是否可用
    *temptr = 'd';
    printf("%c
", *temptr);
    free(temptr);
    return 0;
}

void *helloworld(char *argc)
{
    int *p;
    p = (int *)malloc(10 * sizeof(int));
    printf("the message is %s
", argc);
    printf("the child id is %u
", pthread_self());
    memset(p, 'c', 10);
    printf("p = %x
", p);
    pthread_exit(p);  //退出线程,堆空间首地址做为返回信息
}

可以看到,函数名 helloworld 和 *helloworld 是一样的。

线程退出时资源释放

void pthread_cleanup_push(void (*routine) (void *), void *arg) :压入清理函数栈,后进先出

void pthread_cleanup_pop(int execute) :参数为0,表示不执行弹出的清理函数;非0执行。

#include<pthread.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
void cleanup()
{
    printf("cleanup
");
}
void *test_cancel(void)
{
    pthread_cleanup_push(cleanup, NULL);
    printf("test_cancel
");
    while(1)
    {
        printf("test message
");
        sleep(1);
    }
    pthread_cleanup_pop(1);
}
int main()
{
    pthread_t tid;
    //创建线程
    pthread_create(&tid, NULL, (void *)test_cancel, NULL);
    sleep(2);
    //取消子线程
    pthread_cancel(tid);
    pthread_join(tid, NULL);
}

取消线程

条件1:线程必须可以被其他线程取消,默认可以

条件2:线程处于可取消点才能被取消。

int pthread_cancel (pthread_t __cancelthread) :向某线程发送取消操作。

int pthread_setcancelstate (int __state, int *__oldstate) :设置当前线程的可取消性, state为新状况,oldstate存储原来的状态。

  PTHREAD_CANCEL_DISABLE为不可取消;

  PTHREAD_CANCEL_ENABLE为可取消。(默认值)

int pthread_setcanceltype (int __type, int *__oldtype) :设置取消类型。

  PTHREAD_CANCEL_ASYNCHRONOUS :可随时执行新的或未决的取消请求

  PTHREAD_CANCEL_DEFERRED :在目标线程到达取消点之前,取消请求处于未决状态。(默认值)

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<pthread.h>
void *thread_function(void *arg);
int main(int argc, char *argv[])
{
    int res;
    pthread_t a_thread;
    void *thread_result;
    //创建线程
    res = pthread_create(&a_thread, NULL, thread_function, NULL);
    if(res != 0)
    {
        perror("Thread creation failed");
        exit(EXIT_FAILURE);
    }
    sleep(3);
    printf("Cancelling thread...
");
    //取消子线程
    res = pthread_cancel(a_thread);
    if(res != 0)
    {
        perror("Thread cancelation failed");
        exit(EXIT_FAILURE);
    }
    printf("Waitint for thread to finish...
");
    //等待子线程结束
    res = pthread_join(a_thread, &thread_result);
    if(res != 0)
    {
        perror("Thread join failed");
        exit(EXIT_FAILURE);
    }
    exit(EXIT_SUCCESS);
}

//新线程执行函数
void *thread_function(void *arg)
{
    int i, res, j;
    sleep(1);
    //设置为不可取消
    res = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
    if(res != 0)
    {
        perror("Thread pthread_setcancelstate failed");
        exit(EXIT_FAILURE);
    }
    sleep(3);
    printf("thread cancle type is disable, can't cancle this thread
");
    for(i = 0; i < 3; i++)
    {
        printf("Thread is running (%d)...
", i);
        sleep(1);
    }
    //设置为可取消
    res = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    if(res != 0)
    {
        perror("Thread pthread_setcancelstate failed");
        exit(EXIT_FAILURE);
    }
    printf("Now change the canclestate is ENABLE
");
    sleep(200);
    pthread_exit(0);
}

线程与私有数据

如果需要每个线程有自己私有的全局变量,可以使用同名而不同内存地址的线程私有数据结构,成为私有数据TSD。

int pthread_key_create(pthread_key_t *key, void (*destr_function)(void *)) :创建线程私有数据,地址赋值给key. 所有线程对key可见,但线程可以根据自己的需要,在key中填入不同值,相当于同名不同值。第二个参数表示线程退出时key的资源释放函数。

int pthread_key_delete(pthread_key_t key) :注销一个TSD,不调用destr_sunction?

int pthread_setspecific (pthread_key_t key, const void *pointer) :将pointer的值与key相关联。

void * pthread_getspecific (pthread_key_t key) :读取与key相关联的数据

全局变量测试,不使用TSD的情况。

#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
#include<stdlib.h>
//全局变量,初值100
int key = 100;
void *helloworld_one(char * argc)
{
    printf("the message is %s
", argc);
    //修改值为10
    key = 10;
    printf("key=%d, the child id is %u
", key, pthread_self());
    return 0;
}
void *helloworld_two(char * argc)
{
    printf("the message is %s
", argc);
    //休眠,让另一个线程先修改值
    sleep(1);
    printf("key=%d, the child id is %u
", key, pthread_self());
    return 0;
}
int main()
{
    pthread_t thread_id_one;
    pthread_t thread_id_two;
    pthread_create(&thread_id_one, NULL, (void *)*helloworld_one, "helloworld");
    pthread_create(&thread_id_two, NULL, (void *)*helloworld_two, "helloworld");
    pthread_join(thread_id_one, NULL);
    pthread_join(thread_id_two, NULL);
}

两个线程打印的都是修改后的值

使用TSD,值不同

#include<stdio.h>
#include<pthread.h>
//线程私有数据类型
pthread_key_t key;
void echomsg(void *t)
{
    printf("destructor excuted in thread %u, param=%u
",pthread_self(),((int *)t));
}
void * child1(void *arg)
{
    int i = 10;
    int tid = pthread_self();
    printf("
set key value %d in thread %u
", i, tid);
    //修改私有数据值
    pthread_setspecific(key, &i);
    //等待让另一个线程修改值
    printf("thread one sleep 2 until thread two finish
");
    sleep(2);
    printf("
thread %u returns %d, add is %u
", tid, *((int *)pthread_getspecific(key)),
    (int *)pthread_getspecific(key));
}
void * child2(void *arg)
{
    int temp = 20;
    int tid = pthread_self();
    printf("
set key value %d in thread %u
", temp, tid);
    //修改私有数据值
    pthread_setspecific(key, &temp);
    sleep(1);
    printf("
thread %u returns %d, add is %u
", tid, *((int *)pthread_getspecific(key)),
    (int *)pthread_getspecific(key));
}
int main(void)
{
    pthread_t tid1, tid2;
    pthread_key_create(&key, echomsg);
    pthread_create(&tid1, NULL, (void *)child1, NULL);
    pthread_create(&tid2, NULL, (void *)child2, NULL);
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    pthread_key_delete(key);
    return 0;
    
}

原文地址:https://www.cnblogs.com/dplearning/p/4689648.html