多线程具体实现

多线程实现



小知识:
修改文件夹下所有文件的权限

sudo chmod -R 777 filename

filename为要修改的文件夹名字。-R应该是表示递归修改filename文件夹下所有文件的权限。
其实整个命令的形式是

sudo chmod -(代表类型)×××(所有者)×××(组用户)×××(其他用户)

线程

• 线程有时被称为轻量级进程(LightweightProcess,LWP),是程序执行流的最小单元。
• 线程是进程中的一个实体,是被系统独立调度和分派的基本单位,
• 线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。
在这里插入图片描述

引入线程前, 进程是资源分配的基本单位,也是调度的基本单位引入线程后, 进程是资源分配的基本单位, 线程是调度的基本单位

线程

• 允许程序执行不止一个任务的机制• 并行执行• 受操作系统异步调度, 是操作系统调度的最小单元,• 进程内的不同线程执行是同一程序的不同部分
主线程
• 线程可由进程创建,操作系统在创建进程时会创建一个主线程,程序main()函数是主线程的入口。

在这里插入图片描述

  1. 由于进程的地址空间是私有的,因此在进程间上下文切换时,系统开销比较大, 在同一个进程中创建的线程共享该进程的地址空间,
  2. 每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。
  3. 线程也有就绪、阻塞和运行三种基本状态。
  4. Linux里同样用task_struct来描述一个线程。 线程和进程都参与统一的调度
  5. 多线程通过第三方的线程库来实现 New POSIX ThreadLibrary (NPTL)
    在这里插入图片描述
    在这里插入图片描述

多线程优点

经济实惠
• 分配的资源少、线程切换比进程切换快、维护线程的开销小
资源共享
• 线程共享它们所属进程的存储器和资源。
提高了响应速度
• 允许程序在它的一部分被阻塞或正在执行一个冗长 的操作时持续运行
提高了多处理机体系结构的利用率
• 在多CPU机器中,多线程提高了并发性
线程共享的资源
一个进程中的多个线程共享以下资源
• 可执行的指令
• 静态数据
• 进程中打开的文件描述符
• 信号处理函数
• 当前工作目录
• 用户ID
• 用户组ID

线程私有的资源

每个线程私有的资源如下
• 线程ID (TID)
• PC(程序计数器)和相关寄存器
• 堆栈
局部变量
返回地址
• 错误号(errno)
• 信号掩码和优先级
• 执行状态和属性

线程创建

NPTL线程库中提供了如下基本操作
• 创建线程
• 删除线程
• 控制线程
线程间同步和互斥机制
• 信号量
• 互斥锁
• 条件变量

pthread_create

#include <pthread.h>
 int  pthread_create(pthread_t *thread,  const pthread_attr_tattr,  
                             void * ( routine)(void *),  void *arg)

函数参数
thread:创建的线程
attr:指定线程的属性,NULL表示使用缺省属性
routine:线程执行的函数
arg: 传递给线程执行的函数的参数
函数返回值
成功:0
出错:-1
PTHREAD_CREATE_DETACHED分离线程没有被其他的线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源
PTHREAD _CREATE_JOINABLE线程的默认属性是非分离状态,这种情况下,原有的线程等待创建的线程结束。只有当pthread_join()函数返回时,创建的线程才算终止,才能释放自己占用的系统资源

pthread_join

#include <pthread.h>
 int  pthread_join(pthread_t thread,  void **value_ptr)

线程执行完后如果不join的话,线程的资源会一直得不到释放而导致内存泄漏!

函数参数
thread:要等待的线程
value_ptr:指针*value_ptr指向线程返回的参数
函数返回值
成功:0
出错:-1

pthread_cancel

 #include <pthread.h>

int  pthread_cancel(pthread_t thread)

函数参数
thread:要取消的线程
函数返回值
成功:0
出错:-1

pthread_exit

#include <pthread.h>
int  pthread_exit(void *value_ptr)

函数参数
value_ptr:线程退出时返回的值
函数返回值
成功:0
出错:-1
在不终止整个进程的情况下,单个线程可以有三种方式停止其工作流并退出
1.线程从其工作函数中返回, 返回值是线程的退出码
2.线程可以被同一进程中的其他线程取消
3.线程自己调用pthread_exit

实例

#include <pthread.h>
#include <semaphore.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void *thread1_func(void *arg)
{
    int    n;    
    
    for (n = 0; n < 30; n++) {
        sleep(1);
        printf("AAAAAAAAAA\n");
    }

    pthread_exit(NULL);
    //return NULL;
}

void *thread2_func(void *arg)
{
    int n;

    for (n = 0; n < 30; n++) {
        sleep(1);
        printf("BBBBBBBBBB\n");
    }

    return NULL;
}

int main(int argc, char *argv[])
{
    pthread_t tid1, tid2;
    
    if (pthread_create(&tid1, NULL, thread1_func, NULL) != 0) {
        perror("main: pthread_create thread_1 failed");
        return 1;
    } else {
        printf("main: pthread_create thread_1 succeed!\n");
    }

    if (pthread_create(&tid2, NULL, thread2_func, NULL) != 0) {
        perror("main: pthread_create thread_2 failed");
        return 1;
    } else {
        printf("main: pthread_create thread_2 succeed!\n");
    }
    
    if (pthread_join(tid1, NULL) != 0) {
        perror("main: pthread_join thread_1 failed");
    }
    if (pthread_join(tid2, NULL) != 0) {
        perror("main: pthread_join thread_2 failed");
    }
    
    printf("main is exiting.\n");
    
    return 0;
}

编译

gcc thread.c -o run -lpthread

在这里插入图片描述

进程调度有一定随机性
whereis pthread

在这里插入图片描述
在这里插入图片描述

线程查看

ps –efL

在这里插入图片描述
在这里插入图片描述

共用同一个pid,全局变量共享,打开的文件共享。
如果LWP与PID值相同则为主线程
•UID:用户ID
• PID:process id 进程id
• PPID: parent process id 父进程id
• LWP:表示这是个线程;要么是主线程(进程),要么是线程
• NLWP: num of light weight process 轻量级进程数量,即线程数量
• STIME: start time 启动时间TIME: 占用的CPU总时间
• TTY:该进程是在哪个终端运行的
• CMD:进程的启动命令
ps -ef f
• 用树形显示进程和线程

多线程全局变量共享

注意:如果共享局部变量使用下列方法失败

#include <pthread.h>
#include <semaphore.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void *thread1_func(void *arg)
{
    int    n;    
    int lfp = *(int *)arg;
    for (n = 0; n < 30; n++) {
        sleep(1);
        printf("AAAAAAAAAA\n");
        printf("A lfp = %d\n",lfp);
    }

    pthread_exit(NULL);
    //return NULL;
}

void *thread2_func(void *arg)
{
    int n;
    int lfp = *(int *)arg;

    for (n = 0; n < 30; n++) {
        sleep(1);
        printf("BBBBBBBBBB\n");
        printf("A lfp = %d\n",lfp);
    }

    return NULL;
}

int main(int argc, char *argv[])
{
    pthread_t tid1, tid2;
    int lfp = 10;
    if (pthread_create(&tid1, NULL, thread1_func, &lfp) != 0) {
        perror("main: pthread_create thread_1 failed");
        return 1;
    } else {
        printf("main: pthread_create thread_1 succeed!\n");
    }
    lfp = 15;
    if (pthread_create(&tid2, NULL, thread2_func, &lfp) != 0) {
        perror("main: pthread_create thread_2 failed");
        return 1;
    } else {
        printf("main: pthread_create thread_2 succeed!\n");
    }
    
    if (pthread_join(tid1, NULL) != 0) {
        perror("main: pthread_join thread_1 failed");
    }
    if (pthread_join(tid2, NULL) != 0) {
        perror("main: pthread_join thread_2 failed");
    }
    
    printf("main is exiting.\n");
    
    return 0;
}

如果运行结果
在这里插入图片描述

在线程A中局部变量理想值应该为10,线程B中局部变量理想值才应该为15,实际中两个都为15

解决方法

专门为局部变量申请内存,或尽量不用局部变量,不要将局部变量地址传入线程

#include <pthread.h>
#include <semaphore.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void *thread1_func(void *arg)
{
    int    n;    
    int lfp = *(int *)arg;
    for (n = 0; n < 30; n++) {
        sleep(1);
        printf("AAAAAAAAAA\n");
        printf("A lfp = %d\n",lfp);
    }
    free(arg);
    pthread_exit(NULL);
    //return NULL;
}

void *thread2_func(void *arg)
{
    int n;
    int lfp = *(int *)arg;

    for (n = 0; n < 30; n++) {
        sleep(1);
        printf("BBBBBBBBBB\n");
        printf("B lfp = %d\n",lfp);
        
    }
    free(arg);
    return NULL;
}

int main(int argc, char *argv[])
{
    pthread_t tid1, tid2;
    int lfp = 10;
    int *p = malloc(sizeof(int));
    *p = 10;
    if (pthread_create(&tid1, NULL, thread1_func, p) != 0) {
        perror("main: pthread_create thread_1 failed");
        return 1;
    } else {
        printf("main: pthread_create thread_1 succeed!\n");
    }
    lfp = 15;
    if (pthread_create(&tid2, NULL, thread2_func, &lfp) != 0) {
        perror("main: pthread_create thread_2 failed");
        return 1;
    } else {
        printf("main: pthread_create thread_2 succeed!\n");
    }
    
    if (pthread_join(tid1, NULL) != 0) {
        perror("main: pthread_join thread_1 failed");
    }
    if (pthread_join(tid2, NULL) != 0) {
        perror("main: pthread_join thread_2 failed");
    }
    
    printf("main is exiting.\n");
    
    return 0;
}

在这里插入图片描述

多线程打开文件共享

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <pthread.h>
#include <semaphore.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int fd = -1;
void *thread1_func(void *arg)
{
    char buf[128] = {0};
    read(fd,buf,5);
    printf("thread1: buf = %s\n",buf);


    pthread_exit(NULL);
    //return NULL;
}

void *thread2_func(void *arg)
{
    char buf[128] = {0};
    read(fd,buf,5);
    printf("thread2: buf = %s\n",buf);

    return NULL;
}

int main(int argc, char *argv[])
{
    pthread_t tid1, tid2;
    fd = open("./lfp",O_RDWR);
    if(fd < 0){
        perror("open fail\n");
    }


    if (pthread_create(&tid1, NULL, thread1_func, NULL) != 0) {
        perror("main: pthread_create thread_1 failed");
        return 1;
    } else {
        printf("main: pthread_create thread_1 succeed!\n");
    }
    lfp = 15;
    if (pthread_create(&tid2, NULL, thread2_func, NULL) != 0) {
        perror("main: pthread_create thread_2 failed");
        return 1;
    } else {
        printf("main: pthread_create thread_2 succeed!\n");
    }
    
    if (pthread_join(tid1, NULL) != 0) {
        perror("main: pthread_join thread_1 failed");
    }
    if (pthread_join(tid2, NULL) != 0) {
        perror("main: pthread_join thread_2 failed");
    }
    
    printf("main is exiting.\n");
    
    return 0;
}
 

在这里插入图片描述

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <pthread.h>
#include <semaphore.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int fd = -1;
void *thread1_func(void *arg)
{
    char buf[128] = {0};
    read(fd,buf,5);
    printf("thread1: buf = %s\n",buf);


    pthread_exit(NULL);
    //return NULL;
}

void *thread2_func(void *arg)
{
    fd = open("./lfp",O_RDWR);
    if(fd < 0){
        perror("open fail\n");
    }
    char buf[128] = {0};
    read(fd,buf,5);
    printf("thread2: buf = %s\n",buf);

    return NULL;
}

int main(int argc, char *argv[])
{
    pthread_t tid1, tid2;
    



    if (pthread_create(&tid1, NULL, thread1_func, NULL) != 0) {
        perror("main: pthread_create thread_1 failed");
        return 1;
    } else {
        printf("main: pthread_create thread_1 succeed!\n");
    }

    if (pthread_create(&tid2, NULL, thread2_func, NULL) != 0) {
        perror("main: pthread_create thread_2 failed");
        return 1;
    } else {
        printf("main: pthread_create thread_2 succeed!\n");
    }
    
    if (pthread_join(tid1, NULL) != 0) {
        perror("main: pthread_join thread_1 failed");
    }
    if (pthread_join(tid2, NULL) != 0) {
        perror("main: pthread_join thread_2 failed");
    }
    
    printf("main is exiting.\n");
    
    return 0;
}

在这里插入图片描述

多线程互斥锁

线程与进程的同步互斥比较
• 多线程共享同一个进程的地址空间
• 优点:
线程间很容易进行通信
通过全局变量实现数据共享和交换
• 缺点:
多个线程同时访问共享对象时需要引入同步和互斥机制

互斥锁

互斥锁主要用来保护临界资源
• 每个临界资源都由一个互斥锁来保护,任何时刻最多只能有一个线程能访问
该资源
• 使用规则:
线程必须先获得互斥锁才能访问临界资源,访问完资源后释放该锁。
如果无法获得锁,线程会阻塞直到获得锁为止

mutex函数

pthread_mutex_init
#include <pthread.h>
 int  pthread_mutex_init(pthread_mutex_t  *mutex,pthread_mutexattr_t *attr)

// 初始化互斥锁
函数参数
mutex:互斥锁
attr: 互斥锁属性 // NULL表示缺省属性
函数返回值
成功:0
出错:-1
Posix Mutex API
PTHREAD_MUTEX_TIMED_NP,这是缺省值,也就是普通锁。当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁。这种锁策略
保证了资源分配的公平性。
PTHREAD_MUTEX_RECURSIVE_NP,嵌套锁,允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁。如果是不同线程请求,则在加锁线程解锁时重新竞
争。
PTHREAD_MUTEX_ERRORCHECK_NP,检错锁,如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。这样保
证当不允许多次加锁时不出现最简单情况下的死锁。PTHREAD_MUTEX_ADAPTIVE_NP,适应锁,动作最简单的锁类型,仅等待解锁后重新竞争。

pthread_mutex_unlockpthread_mutex_lock

 #include <pthread.h>
 int  pthread_mutex_unlock(pthread_mutex_t *mutex)// 释放互斥锁

函数参数
mutex:互斥锁
函数返回值
成功:0
出错:-1

 #include <pthread.h>
 int  pthread_mutex_lock(pthread_mutex_t *mutex)// 申请互斥锁

函数参数
mutex:互斥锁
函数返回值
成功:0
出错:-1

举例

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

//#define _LOCK_
unsigned int value1,value2, count=0;
pthread_mutex_t  mutex;
void *function(void *arg);

int main(int argc,  char *argv[])
{
    pthread_t  a_thread;
         
    if (pthread_mutex_init(&mutex, NULL) < 0)                                                                                                                                                          
    {
        perror("fail to mutex_init");
        exit(-1);
    }

    if (pthread_create(&a_thread, NULL, function, NULL) < 0)
    {
        perror("fail to pthread_create");
        exit(-1);
    }
    while ( 1 )
    {
        count++;
//#ifdef  _LOCK_
 //       pthread_mutex_lock(&mutex);
//#endif
        value1 = count;
        value2 = count;
//#ifdef  _LOCK_
//       pthread_mutex_unlock(&mutex);
//#endif
    }
    return 0;
 }

void  *function(void *arg)
{
     while ( 1 )
     {
#ifdef _LOCK_
        pthread_mutex_lock(&mutex);
#endif           

        if (value1 != value2)  
        {
            printf("count=%d , value1=%d, value2=%d\n",  count, value1, value2);
            usleep(100000);
        }     
#ifdef _LOCK_
        pthread_mutex_unlock(&mutex);
#endif
     }
     return  NULL;
 }     

在这里插入图片描述

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

//#define _LOCK_
unsigned int value1,value2, count=0;
pthread_mutex_t  mutex;
void *function(void *arg);

int main(int argc,  char *argv[])
{
    pthread_t  a_thread;
         
    if (pthread_mutex_init(&mutex, NULL) < 0)                                                                                                                                                          
    {
        perror("fail to mutex_init");
        exit(-1);
    }

    if (pthread_create(&a_thread, NULL, function, NULL) < 0)
    {
        perror("fail to pthread_create");
        exit(-1);
    }
    while ( 1 )
    {
        count++;
#ifdef  _LOCK_
        pthread_mutex_lock(&mutex);
#endif
        value1 = count;
        value2 = count;
#ifdef  _LOCK_
        pthread_mutex_unlock(&mutex);
#endif
    }
    return 0;
 }

void  *function(void *arg)
{
     while ( 1 )
     {
#ifdef _LOCK_
        pthread_mutex_lock(&mutex);
#endif           

        if (value1 != value2)  
        {
            printf("count=%d , value1=%d, value2=%d\n",  count, value1, value2);
            usleep(100000);
        }     
#ifdef _LOCK_
        pthread_mutex_unlock(&mutex);
#endif
     }
     return  NULL;
 }     

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

条件变量

• 条件变量是利用线程间共享的全局变量进行同步的一种机制。
• 主要包括两个动作:
一个线程等待“条件变量的条件成立”而挂起;
另一个线程使“条件成立”(给出条件成立信号)并唤醒挂起线程。
• 为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起。对条件的测试是在互斥锁(互斥)的保护下进行的。

条件变量创建和注销

  #include <pthread.h>
 int  pthread_cond_destroy(pthread_cond_t *cond)

函数参数
cond:条件变量
函数返回值
成功:0
出错:返回错误码

#include <pthread.h>
 int  pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t   *cond_attr)

函数参数
cond:条件变量
attr :通常为NULL。默认值是 PTHREAD_ PROCESS_PRIVATE,即此条件变量被同一进程内的各个线程使用。
函数返回值
成功:0
出错:返回错误码

条件变量等待

#include <pthread.h>
int  pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t   *mutex)

函数参数
cond:条件变量
mutex :互斥锁
函数返回值
成功:0
出错:返回错误码

条件变量唤醒

#include <pthread.h>
 int  pthread_cond_signal(pthread_cond_t *cptr)
//按入队顺序唤醒其中一个
int  pthread_cond_broadcast(pthread_cond_t *cptr)
唤醒所有等待线程

函数参数
cond:条件变量
函数返回值
成功:0
出错:返回错误码

实例

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

pthread_mutex_t mutex;//锁定标志 
pthread_cond_t cond;//唤醒条件变量


void *thread1_func(void *arg)
{
    int    n;    
    printf("enter t1\n");
    pthread_mutex_lock(&mutex);//锁定    
    printf("t1 lock mutex\n");
    for (n = 0; n < 3; n++) {
        sleep(1);
        printf("AAAAAAAAAA\n");
    }

    pthread_cond_signal(&cond);//唤醒单个线程
    printf("t1 signal cond to t2\n");    
    pthread_mutex_unlock(&mutex);//解锁
    printf("t1 unlock mutex\n");    

    pthread_exit(NULL);
    //return NULL;
}

void *thread2_func(void *arg)
{
    int n;
    
    printf("\t\tenter t2\n");

    pthread_mutex_lock(&mutex);//锁定, 阻塞前被锁上
    printf("\t\tt2 lock mutex and wait\n");    
    pthread_cond_wait(&cond,&mutex);
    printf("\t\tt2 wakeup\n");
    for (n = 0; n < 3; n++) {
        sleep(1);
        printf("\t\tBBBBBBBBBB\n");
    }
    pthread_mutex_unlock(&mutex);//解锁
    printf("\t\tt2 unlock mutex\n");    

    return NULL;
}

int main(int argc, char *argv[])
{
    pthread_t tid1, tid2;

    //生成互斥锁mutex,默认属性初始化
    pthread_mutex_init(&mutex,NULL);
    
    //生成一个唤醒变量,默认属性=?同一进程内的所有线程使用
    pthread_cond_init(&cond,NULL);

    
    if (pthread_create(&tid1, NULL, thread1_func, NULL) != 0) {
        perror("main: pthread_create thread_1 failed");
        return 1;
    } else {
        printf("main: pthread_create thread_1 succeed!\n");
    }

    if (pthread_create(&tid2, NULL, thread2_func, NULL) != 0) {
        perror("main: pthread_create thread_2 failed");
        return 1;
    } else {
        printf("main: pthread_create thread_2 succeed!\n");
    }
    
    if (pthread_join(tid1, NULL) != 0) {
        perror("main: pthread_join thread_1 failed");
    }
    if (pthread_join(tid2, NULL) != 0) {
        perror("main: pthread_join thread_2 failed");
    }
    
    pthread_cond_destroy(&cond);//释放阻塞唤醒变量
    pthread_mutex_destroy(&mutex);//释放互斥锁mutex资源
    printf("main is exiting.\n");
    
    return 0;
}

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

苦梨甜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值