Linux基础(15)多线程编程

摘要:
Linux内核没有线程的概念。线程由第三方库libpthread实现,这有点类似于vfork(轻量级过程,仅部分复制)(进程创建分叉将完全复制主进程资源,而线程将共享资源。当子线程创建新资源时,其应用程序域仅在当前子线程中,而非新创建的子线程资源将在创建前与主线程共享这些资源),线程和进程的创建是kernel_Process中的系统调用副本,但它们的实现不同。进程将调用fork(

Linux的内核中没有thread的概念,线程是第三方库libpthread实现的, 和vfork(轻量级进程,只有部分copy)有点像(进程的创建fork会完全copy主进程资源 ,而线程会共享资源,子线程创建新资源时其作用域只在当前子线程,而子线程非新新创建的资源会和创建前的主线程共享这些资源) , 线程和进程的创建在内核里都是系统调用copy_process ,但是他们的实现不同, 进程会调用fork()的copy_process(全拷贝) ,而线程调用的是thread的copy_process(第三方库实现的,只拷贝部分) , copy_process是一个虚函数

小知识: Linux 有个内存文件系统挂载在内存里(shell:cd /proc)用作调试使用, 里面有很多数,这些是进程或线程的ID, 这些文件里有他们的调试信息,比如 ps -ef 所展示的信息

    shell:cd空格/work/pthread , 在这下面使用 pmap(进程映射工具)把进程信息保存(pmap [ThreadNum] > a.txt)

    pmap -x  [ThreadNum] >a.txt

    pmap -x  [ThreadNum] >b.txt

    可以使用shell:diff a.txt b.txt  比对保存的两个进程或线程信息 有何区别

 线程的概念  进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位

       (进程只是系统分配的资源而执行的是线程(在没有创建子线程时进程默认有一个线程执行,创建子线程后默认的线程作为主线程))

  什么是堆 ,什么是栈 ,他们在哪  https://www.cnblogs.com/valor-xh/p/6535421.html

  线程不代表资源,是程序的最小执行单元-----每一个线程都有一个栈 ,当线程结束时栈会被回收

  进程代表资源,是程序调度的最小单元------创建的子线程共享主进程的一切资源, 而子进程新创建的资源不与主线程和其他线程共享

  多线程是并发执行的,但也是有数量限制的,过了时间片要等待下一次的CPU调度 线程和进程除了资源的区别其他和进程一样

  多线程的并行其实是伪并行,CPU的调度是有轮转的,比如A线程被CPU调度后,CPU寄存器等是被A所占用的,而A过了时间片后其状态和信息(寄存器里的值)会被保存在tss段, 而B线程会的状态和信息会被恢复到CPU

  Linux基础(15)多线程编程第1张

线程的管理

  gcc -g -o xxx xxx.c -lpthread

  pthread_self()     获得线程tid(由线程库维护的 ,其ID空间的各个进程独立的, ,因此每个进程的线程可能会相同)

  syscall(SYS_gettid),  获取内核中的线程ID(内核中没有线程的概念只有轻量级进程 ,SYS_gettid则是获取内核中这个轻量级进程的ID 是唯一的)

   syscall: http://blog.chinaunix.net/uid-28458801-id-4630215.html

  线程的创建

    int pthread_create(pthread_t *thread , const pthread_attr_t *attr , void(start_routine)(void*) , void *arg );

      pthread_t *thread     线程的id 放一个指针,线程创建完后作为返回值返回给 传入的指针 返回指向线程的首地址

      const pthread_attr_t *attr  NULL 线程的属性

       void(start_routine)(void*)  线程的执行函数

       void *arg         传递给线程函数的参数

  线程的退出与等待

    void pthread_exit(void *retval);  自行退出当前线程  retval是一个返回值,可以NULL

    其他线程调用 pthread_cancel

    线程执行完后也是自动退出 , 创建线程的进程退出后线程也会退出

    其中一个线程执行了exec类函数,因为会替换当前进程的所有的地址空间 线程也会退出

    int pthread_join(pthread_t thread, void **retval); 等待指定线程退出后,会收到指定线程退出时返回的值 ,也可以NULL不接收

Linux基础(15)多线程编程第2张Linux基础(15)多线程编程第3张
#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void *helloworld(char *argc);
int main(int argc,int 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);                                                //把堆的指针返回出去
    //return 0;
}
thread_exit_ join

  线程的取消  

    线程能否被取消要看两点:

      线程是否具有可取消属性---默认可以被取消

      线程如果设置为到可取消点才能被取消时,线程被取消是不会被立刻取消

    int pthread_cancel(pthread_t thread);

    线程的取消状态属性

      int pthread_setcancelstate(int state, int *oldstate);

        state : PTHREAD_CANCEL_ENABLE 可取消属性

           PTHREAD_CANCEL_DISABLE---->不可取消

    线程的取消类型属性  A线程收到了B线程的取消请求时请求时 ,要根据取消类型判断是立即取消飞行合适再取消(取决于系统)

       int pthread_setcanceltype(int type, int *oldtype);      

        type: 立刻被取消 PTHREAD_CANCEL_ASYNCHRONOUS

            只有到达一定取消点,才会取消 PTHREAD_CANCEL_DEFERRED

  线程的私有数据

    TSD私有数据,同名但是不同内存地址的私有数据结构, 比如fork()后子进程继承了父进程的一切包括全局变量的值,但是在子进程修改了这个全局变量却不影响父进程的全局变量的值, 父进程的这个全局变量也不影响子进程的值, 两者不在同一内存地址互不影响

    https://blog.csdn.net/mengxingyuanlove/article/details/50802246

    int pthread_key_create(pthread_key_t key, void (destructor)(void*)); 创建

    int pthread_key_delete(pthread_key_t key);           删除

    void *pthread_getspecific(pthread_key_t key);          获取

    int pthread_setspecific(pthread_key_t key, const void *value);   设置

Linux基础(15)多线程编程第4张Linux基础(15)多线程编程第5张
//this is the test code for pthread_key 

#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);                 //创建线程私有的变量,每个线程都可以继承这个变量,但是内存地址不同而互不影响 ,在线程结束后会调用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; 
} 
pthread_key_test

线程占有的都是不共享的,其中包括:栈、寄存器、状态、程序计数器

线程间共享的有:堆,全局变量,静态变量;


            线程的互斥的方式与更精细的互斥: 互斥锁,条件变量 , 读写锁

    互斥锁的通信机制

      pthread_mutex_t                  互斥锁对象

      int pthread_mutex_destroy(pthread_mutex_t *mutex);  销毁对象

      int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);    初始化对象

      int pthread_mutex_lock(pthread_mutex_t *mutex);    上锁

      int pthread_mutex_trylock(pthread_mutex_t *mutex);   非阻塞函数,无论是否上锁都直接返回

      int pthread_mutex_unlock(pthread_mutex_t *mutex);   解锁

       无论读写共享内存都要先上锁,读完写完都要解锁

Linux基础(15)多线程编程第6张Linux基础(15)多线程编程第7张
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <string.h>

void *thread_function(void *arg);

pthread_mutex_t work_mutex;             //互斥锁

#define WORK_SIZE 1024
char work_area[WORK_SIZE];                //共享内存区域
int time_to_exit = 0;                    //是否退出的信号

int main(int argc,char *argv[]) 
{
    int res;
    pthread_t a_thread;
    void *thread_result;
    res = pthread_mutex_init(&work_mutex, NULL);         //初始化并创建锁对象
    if (res != 0) 
    {
        perror("Mutex initialization failed");
        exit(EXIT_FAILURE);
        }
        res = pthread_create(&a_thread, NULL, thread_function, NULL);        //创建线程
    if (res != 0) 
    {
        perror("Thread creation failed");
        exit(EXIT_FAILURE);
    }
    pthread_mutex_lock(&work_mutex);                                        //上锁
    printf("Input some text. Enter 'end' to finish
");
    while(!time_to_exit)                                                     //判断是否输入了end,如果为1则退出循环写                
    {
        fgets(work_area, WORK_SIZE, stdin);        //get a string from stdin    //往共享区域里写
        pthread_mutex_unlock(&work_mutex);        //unlock the mutex            //解锁
        while(1) 
        {
            pthread_mutex_lock(&work_mutex);                                //上锁
            if (work_area[0] != '

免责声明:内容来源于网络,仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇A Bug's Life调用腾讯微博开放平台出现的错误和解决办法下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

《深入理解Java内存模型》读书总结

概要 文章是《深入理解Java内容模型》读书笔记,该书总共包括了3部分的知识。 第1部分,基本概念 包括“并发、同步、主内存、本地内存、重排序、内存屏障、happens before规则、as-if-serial规则、数据依赖性、顺序一致性模型、JMM的含义和意义”。 第2部分,同步机制 该部分中就介绍了“同...

vmware 安装 kali linux 系统到U盘 启动错误(initramfs:) 修复方法

This article was adapted from https://www.cnblogs.com/xuandi/p/6746880.html 安装kalilinux到U盘 启动之后出现这个错误: 这是grub路径不对。 解决办法: 在这模式下可以输入命令:blkid 查看所列举出的磁盘 找到你的U盘系统 TYPE="ext4"的区 记下  /d...

linux基础学习(二)ls命令以及文件访问权限例(-rw-r-r--)

ls命令 命令选项 作用 ll 显示文件得详细信息 ls -a 显示文件和隐藏文件 ls -t 以文件和目录的更改时间排序 ls -m 用“,”号区隔每个文件目录名称 ll 是ls-l的一个别命 -rw-r-r--. 1 root root 214 4月 17 20:54 mytest1 最左面的-rw-r-r--表示用户对文...

RTT学习之线程管理

获得线程:rt_thread_t rt_thread_self(void);一 线程的创建和删除:rt_thread_create()创建的句柄,对应的删除rt_thread_delete(),注意线程的删除只是将线程的状态该为close,进入空闲任务才删除。rt_thread_init()函数对应的是rt_thread_detach()二 线程的就绪和挂...

《深入理解JAVA虚拟机》(一) JVM 结构 + 栈帧 详解

​ 1、程序计数器(Program Counter Register)         线程独有,每个线程都有自己的计数器;由于CPU的任意时刻只能执行所有线程中的一条,所以需要使用程序计数器来支持JVM的并发;另外字节码解释器读取下一行指令、分支、循环、跳转、异常处理等等逻辑都依赖于程序计数器。程序计数器是JVM唯一不存在OutOfMemoryError...

linux随笔:安装软连接时:ln: failed to create symbolic link ‘/usr/bin/python3/python3’: File exists

在云服务上面安装python: 1.安装软连接时报:ln: failed to create symbolic link ‘/usr/bin/python3/python3’: File exists 2.解决方法:删除软连接 rm /usr/bin/python3 重新安装软连接:ln -s /root/python36/bin/python3 /usr...