zoukankan      html  css  js  c++  java
  • 【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;
        
    }

  • 相关阅读:
    时间复杂度计算
    SQL Server2012编程入门经典(第四版) 读书笔记
    一些编程试题
    Qt 对话框显示控制按钮
    vc++创建文件目录
    配置ubuntu虚拟机备忘
    Qt QThread 多线程使用
    Qt 程序等待多长时间执行Sleep
    Qt 数字和字符处理总结
    c++ 文件utf-8格式
  • 原文地址:https://www.cnblogs.com/dplearning/p/4689648.html
Copyright © 2011-2022 走看看