线程(上)

线程的概念

在Linux中实际上没有真正意义上的线程,它是根据进程的模型模拟出来的一种类似于进程一样的程序,它是在进程的地址空间中存在,并且可以吧进程中的任务分开执行,达到真正意义上的并行,加快操作系统的运行速度;所以说:线程是Linux中轻量级的进程。(在Windows中实现了真正意义上的线程)
因为,线程存在于进程的地址空间中,所以,线程可以共享进程的一些资源和环境:
1、文件描述表,2、每种信号的处理方式;3、当前的工作目录;4、用户ID和组ID
但是也有自己私有的资源是各个线程所不能共享的:
1、线程ID;

2、上下文,包括各种寄存器的值、程序计数器和栈指针
3、栈空间(存储的是函数)

4、errno变量;
5、信号屏蔽字;
6、调度优先级;
所以;我们可以知道:

1、进程是分配系统资源的基本单位(进程强调独占系统资源),调度的基本单位是线程(线程强调共享系统两个资源)
2、线程在进程的地址空间中执行;
3、Linux中国没有真正意义上的线程;

因为在线程是有POSIX标准定义的,所以我们在进行编译的时候要加上-lpthread;
这里写图片描述

线程控制

创建线程

int pthread_create(pthread_t *thread, const pthread_attr_t *arr, void *(*start_rountine)(void *), void *arg);
*thread 为线程的ID,只在用户区有效
const pthread_attr_t *arr 为线程的属性,一般为NULL;
void *(*start_rountine)(void *) 为新线程的函数名
void *arg 为新线程的函数参数

这里写图片描述
在线程中,新线程何主线程的调用也是随机的,并不是系统指定先调用那个;

终止线程

1、从线程中调用return,不适合用于主线程;
2、可以调用pthread_cancel终止同一个进程中的线程;(别的线程终止指定线程)
3、线程可以调用pthread_exit终止自己

在线程终止后都有特定的返回值,会通过线程等待函数join来接受:
这里写图片描述
在return和exit函数中返回值是自己可以指定的,在取消线程pthread_cancel中返回值是特定的-1;

线程等待

int pthread_join(pthread_t thread, void **retval);
thread 线程ID
**retval为返回值它是个二级指针

对于上述终止线程中的各个函数的返回值是有线程等待函数join来接受的,并且返回终止线程的函数指定的返回值;
接受**retval的时候,定义一个一级指针,然后取地址去接受,在输出的时候直接强制转换为int型的来输出:
因为:新线程中是一个一级指针,在主线程中用一级指针的地址去接受,那么这个一级指针的值就和返回值里面的值是一样的,所以转换为int型可以直接输出;
附上全部代码:
my_pthread.c

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

void* thread_run()//new thread
{
    while(1)
    {
        pthread_detach(pthread_self());
        printf("new thread: pid: %lu, tid: %lu\n", getpid(), pthread_self());
//      return (void*)1;//利用return终止线程
//      pthread_exit((void*)2);//利用pthread_exit终止线程
    }
}

int main()
{
    pthread_t id;
    void* val;
    int ret = pthread_create(&id, NULL, thread_run, NULL);
    if(ret != 0){
        printf("pthread_creat error! %d\n", strerror(ret));
        return ret;
    }
    pthread_cancel(id);//利用pthread_cancel进行终止线程
    pthread_detach(id);
    pthread_join(id, &val);
//  printf("main thread: pid: %lu, tid: %lu\n, ", getpid(), pthread_self());
    printf("main thread: pid: %lu, tid: %lu, return code: %d\n", getpid(), pthread_self(), (int)val);

    sleep(1);
    return 0;
}

Makefile

my_pthread:my_pthread.c
    gcc -o $@ $^ -lpthread 

.PHONY:clean
clean:
    rm -f my_pthread

线程分离

线程可分为可结合的或者分离的,一个可结合的线程能够被其他线程回收期资源和杀死,在其他线程回收前,它的存储器资源是不释放的,一个可分离的线程是不嗯呢该被其他线程回收或杀死的,它的存储器资源在他终止时有系统自动释放。
默认创建的线程是可结合的;

可结合的线程你都应该要被显示的回收,即调用pthread_join;要么替换调用pthread_detach函数分离
如果可结合的线程结束运行没有被join,可能会导致类似与僵尸进程的状态;
在主线程永不退出的情况下:可以有两种方法来设置分离

pthread_detach(pthread_self())(线程自己设置为分离)
pthread_detach(thread_id)(由主线程将制定ID的线程设置为分离,程序运行结束后自动释放所有资源)

当分离成功后,该线程不依赖与主线程,所以主线程join接收时,就会出现等待失败的情况;

线程同步与互斥

mutex(互斥量)

mutex和前面的 semaphore(信号量)差不多,都是一把锁,用来保护临界资源在执行的时候不被别的线程所中断,所以在多线程的程序中,我们就能保护临界资源;

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;(创建一个全局变量的互斥锁)
pthread_mutex_t mutex;
pthrad_once_t foo_once = PTHREAD_ONCE_INIT;
(创建一个局部变量的互斥锁)
int pthread_mutex_lock(pthrad_mutex_t *mutex);(在临界区前面加锁)
int pthread_mutex_unlock(pthread_mutex_t *mutex);(在临界区后去掉锁)

用10个线程来对临界区进行访问,并且在加锁后

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

int g_count = 0;
//pthread_mutex_t Mutex = PTHREAD_MUTEX_INITIALIZER;//全局锁
static pthread_mutex_t Mutex;
static pthread_once_t foo_once = PTHREAD_ONCE_INIT;
void init_foo()
{
    pthread_mutex_init(&Mutex, NULL);//局部锁
}
void* thread_run(void* _val)
{
    pthread_once(&foo_once, init_foo);
//  pthread_mutex_init(&Mutex,  NULL);
    int val = 0;
    int i = 0;
    while(i++ < 5000){
        pthread_mutex_lock(&Mutex);
        val = g_count;
        printf("thread id : %lu, g_count: %d\n", pthread_self(), g_count);
        g_count = val + 1;
        pthread_mutex_unlock(&Mutex);
    }
    return NULL;
}

int main()
{
    pthread_t id0;
    pthread_t id1;
    pthread_t id2;
    pthread_t id3;
    pthread_t id4;
    pthread_t id5;
    pthread_t id6;
    pthread_t id7;
    pthread_t id8;
    pthread_t id9;
    pthread_create(&id0, NULL, thread_run, NULL);
    pthread_create(&id1, NULL, thread_run, NULL);
    pthread_create(&id2, NULL, thread_run, NULL);
    pthread_create(&id3, NULL, thread_run, NULL);
    pthread_create(&id4, NULL, thread_run, NULL);
    pthread_create(&id5, NULL, thread_run, NULL);
    pthread_create(&id6, NULL, thread_run, NULL);
    pthread_create(&id7, NULL, thread_run, NULL);
    pthread_create(&id8, NULL, thread_run, NULL);
    pthread_create(&id9, NULL, thread_run, NULL);
    pthread_join(id0, NULL);
    pthread_join(id1, NULL);
    pthread_join(id2, NULL);
    pthread_join(id3, NULL);
    pthread_join(id4, NULL);
    pthread_join(id5, NULL);
    pthread_join(id6, NULL);
    pthread_join(id7, NULL);
    pthread_join(id8, NULL);
    pthread_join(id9, NULL);

    printf("main g_count: %d\n", g_count);
    return 0;

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值