05.线程

进程有哪些缺陷?

1.创建的代价较高

进程是OS进行资源分配的基本单位,也就是说创建进程是需要分配资源的,所以创建代价很高

2.通信不方便

进程之间要想进行通信必须借助第三方资源(管道、内存映射、消息队列)

线程的优点有哪些?

线程与进行一样,在单核的情情况下都可以并发执行,多核可以并行执行。
但是线程的创建代价没有进程高,一个进程可以创建多个线程,这写线程共享同一份资源。共享同一份资源自然,通信就方便了很多

pthread_create函数

创建线程的函数

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

参一:传出参数,子线程tid
参二:线程的属性(attr)
参三:函数指针,子进程要执行的代码放在这个函数里面
参四:子进程函数的参数
返回值:
成功返回0
失败返回错误号
#include<stdio.h>
#include<string.h>                                                              
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
void* thread1(void* arg){
    sleep(1);
    printf("thread1 = %lu\n",pthread_self());
    return NULL;
}

int main(int argc, char* argv[])
{
    pthread_t tid;
    int ret = pthread_create(&tid,NULL,thread1,NULL);
    if(ret == -1){
        perror("pthread_create error:");
        return -1;
    }
    printf("main therad = %lu\n",pthread_self());
    //%lu是无符号场整形 unsigned long int 
    while(1);
    return 0;
}

pthread_self函数

获取线程tid的函数

pthread_t pthread_self(void)

返回值和无符号场整形,%lu
#include<stdio.h>
#include<string.h>                                                              
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
#include<sys/stat.h>
int a;
void* thread1(void* arg){
    printf("thread1 tid = %lu\n",pthread_self());
    while(a!=10){
        a++;
        printf("a = %d\n",a);
        sleep(1);
    }
    return NULL;
}
int main(int argc, char* argv[])
{
    pthread_t tid;
    int ret = pthread_create(&tid,NULL,thread1,NULL);
    if(ret != 0){
        perror("pthread_create error:");
        return -1;
    }
    printf("main thread tid = %lu\n",pthread_self());
    while(a!=10){
        a++;
        printf("a = %d\n",a);
        sleep(1);
    }
    printf("pthread_create ret = %d\n",ret);
    return 0;
}

终止线程的方法

1.进程结束了,底下的线程肯定都结束了

在主函数执行return

#include<stdio.h>
#include<string.h>                                                              
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
#include<sys/stat.h>
int a;
void* thread1(void* arg){
    while(a!=10){
        a++;
        printf("a = %d\n",a);
        sleep(1);
    }
    return NULL;
}
int main(int argc, char* argv[])
{
    pthread_t tid;
    pthread_create(&tid,NULL,thread1,NULL);
    sleep(1);
    return 0;
}

调用exit()函数

#include<stdio.h>
#include<string.h>                                                              
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<stdlib.h>
int a;
void* thread1(void* arg){
    while(a!=10){
        a++;
        printf("a = %d\n",a);
        sleep(1);
    }
    return NULL;
}
void* thread2(void* arg){
    sleep(2);
    exit(-1);
    return NULL;
}
int main(int argc, char* argv[])
{
    pthread_t tid;
    pthread_create(&tid,NULL,thread1,NULL);
    pthread_create(&tid,NULL,thread2,NULL);
    while(1);
    return 0;
}

2.结束当前的线程

在start_rountine函数中执行return

#include<stdio.h>
#include<string.h>                                                              
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<stdlib.h>
int a;
void* thread1(void* arg){
    while(a!=10){
        a++;
        printf("a = %d\n",a);
        return NULL;
        printf("1\n");
    }
    
}
int main(int argc, char* argv[])
{
    pthread_t tid;
    pthread_create(&tid,NULL,thread1,NULL);
    sleep(2);
    return 0;
}

调用pthread_exit()函数

#include<stdio.h>
#include<string.h>                                                              
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<stdlib.h>
int a;
void func(){
    printf("This is func!\n");
    pthread_exit(NULL);
}
void* thread1(void* arg){
    while(a!=10){
        a++;
        printf("a = %d\n",a);
        func();
        printf("1\n");
    }
    
}
int main(int argc, char* argv[])
{
    pthread_t tid;
    pthread_create(&tid,NULL,thread1,NULL);
    sleep(2);
    return 0;
}

调用pthread_cancel函数

int pthread_cancel(pthread_t thread);
参一为要取消的线程
函数描述:调用该函数之后,会向指定线程发送一个取消请求,然后立即返回,被请求取消的进程需要等到某个取消点(通常为系统调用)
可以调用pthread_testcancel()函数,手动创建取消点
#include<stdio.h>
#include<string.h>                                                              
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<stdlib.h>
int a;
int stats;

void* thread1(void* arg){
    while(1){
        if(a == 10){
            a=0;
        }
        a++;
        pthread_testcancel();
    }
}
int main(int argc, char* argv[])
{
    pthread_t tid;
    pthread_create(&tid,NULL,thread1,NULL);
    int ret = pthread_cancel(tid);
    if(ret == 0){
        printf("cancel sucess!\n");
    }else{
        printf("cancel error!\n");
    }
    while(1){
        printf("a = %d\n",a);
        sleep(1);
    }
    return 0;
}

线程的连接和分离

pthread_join函数

函数原型:int pthread_join(pthread_t tid ,void **retval);

函数描述:等待指定线程的结束,然后回收,这种操作被称为连接。未连接的进程会产生僵尸线程

#include<stdio.h>
#include<string.h>                                                              
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<stdlib.h>
int a;

void* thread1(void* arg){
    while(1){
        if(a == 10){
            pthread_exit(NULL);
        }
        a++;
    }
}
int main(int argc, char* argv[])
{
    pthread_t tid;
    void* res;
    pthread_create(&tid,NULL,thread1,NULL);
    pthread_join(tid,&res);
    printf("res = %s",(char*)res);
    printf("a = %d\n",a);
    return 0;
}

有些线程不关心返回状态,只是希望OS能在线程终止的时候自动清理并移出,这时可以调用pthread_detach函数设置线程未分离状态

pthread_detach函数

#include<stdio.h>
#include<string.h>                                                              
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<stdlib.h>
int a;

void* thread1(void* arg){
    while(1){
        if(a == 10){
            pthread_exit(NULL);
        }
        a++;
    }
}
void* thread2(void* arg){
    while(1){
        if(a == 10){
            pthread_exit(NULL);
        }
        a++;
    }
}
int main(int argc, char* argv[])
{
    pthread_t tid1;
    pthread_t tid2;
    void* res;
    pthread_create(&tid1,NULL,thread1,NULL);
    pthread_create(&tid2,NULL,thread2,NULL);
    pthread_detach(tid2);
    pthread_join(tid2,&res);
    printf("res = %s",(char*)res);
    printf("a = %d\n",a);
    return 0;
}

练习

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

#include<stdio.h>
#include<string.h>                                                              
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<stdlib.h>
const int N=1e6;
int a;
void* thread1(void* arg){
    for(int i = 1;i <= N; i++){
        a++;
    }
}
int main(int argc, char* argv[])
{
    pthread_t tids[10];
    //创建10个线程,用tids数组存储tid
    for(int i = 0;i < 10; i++){
        pthread_create(tids+i,NULL,thread1,NULL);
    }
    //阻塞等待10个线程都执行结束,打印a的值
    for(int i = 0;i < 10; i++){
        pthread_join(tids[i],NULL);
    }
    printf("a = %d\n",a);
    return 0;
}

观察数据

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

线程同步的引入

观察上面的数据,发现10个线程如果同时执行1e6次自增操作,最后结果应该是1e7,但是多次得到的结果确实小于等于1e7,这种情况是不可以接受的,为什么会出现这种问题?

因为线程之间的并发执行的,且a++不是原子操作。
举一个例子,现在线程1和线程2并发执行,它们同时读取了a的数据,读到的都是5,然后对5+1返回结果给a,线程1和线程2同时结束,a的值为6,而不是7,这就出现了问题。出现这个问题的原因是线程之间是共享资源的,同时访问一块内存,有时会造成错误。(保证线程和线程访问同一块空间的时候不出现冲突)
所以得让进程之间商量着来,协同一下步调。

同步

同步分为同步异步的同步和线程同步

同步与异步

提到同步和异步,很难不想到阻塞和非阻塞。

阻塞和非阻塞一半表述进程的状态,具体指的是进程在执行过程中是否遇到阻塞实现(是否被挂起),如果被挂起了就是阻塞状态,没有被挂起就是非阻塞

而同步异步一般指的是系统调用时候是否会阻塞执行

同步函数

同步函数是指在系统调用的过程中,程序会被阻塞,知道系统调用结束返回结果。
特点:
1.阻塞执行
2.可预测,符合传统程序的执行过程

异步函数

异步函数指的是系统调用过程中,程序不会阻塞,继续执行后面的代码。等到系统调用结束之后,内核会通过某种机制(信号或者回调函数等等)通知程序。
特点:
1.非阻塞:不会阻塞等待返回的结果,而是直接执行后面的代码。
2.事件驱动性:当系统调用完成时,内核会通过事件通知应用程序,应用程序需要处理这些事件
3.复杂性:异步编程模型复杂,需要处理多个事件和状态
  • 9
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值