Linux 多线程Posix详解(一) : 线程的创建、等待、退出、取消与清理

Posix多线程系列文章:
Linux 多线程Posix详解(一) : 线程的创建、等待、退出、取消与清理
Linux 多线程Posix详解(二) : 线程的同步与互斥
Linux 多线程Posix详解(三) : C++封装


前言

不论是子线程还是主线程,实质上都共同属于同一个进程,这大大的减小了系统的开销。
另外,多线程与多进程的使用实质上主要掌握 创建 等待 退出 这三个点

创建等待退出
多进程forkwaitexit
多线程pthread_createpthread_joinpthread_exit

同时,多线程也有自己的线程清理函数 pthread_cleanup_push(pop).

一.线程创建 pthread_create

int pthread_create(pthread_t *__restrict__ __newthread, 
const pthread_attr_t *__restrict__ __attr, 
void *(*__start_routine)(void *), 
void *__restrict__ __arg)
  • 第一个参数:传入pthread_t 创建的线程的地址;
  • 第二个参数:_attr, 线程的属性地址;
  • 第三个参数:线程的操作函数地址;
  • 第四个参数:传入函数中的参数地址;

线程创建的过程中有可能需要向子线程传入一些参数类型,那么如何进行传入参数也是多线程的难点之一。

注意:因为创建子线程并执行函数需要一定的时间,但是主线程却会一直往下执行,有可能主线程已经执行完成了,但是子线程却都还没有创建好!


  1. 无输入参数
    可以看出,主线程与子线程属于同一个进程
void* threadFunc1(void* arg)
{
    cout << "this is child thread, process id = " << getpid() << " , thread id = " << pthread_self() << endl;
    pthread_exit(NULL);
}

void test1()
{
    pthread_t pid;
    cout << "this is main thread, process id = " << getpid() << " , thread id = " << pthread_self() << endl;
    pthread_create(&pid, NULL, threadFunc1, NULL);
    cout << "child thread id = " << pid << endl;
    pthread_join(pid, NULL);
}

2.输入int型参数

void* threadFunc2(void* arg)
{
    cout << "child thread get val:" << *(int*)arg << endl;
    pthread_exit(NULL); 
}

void test2()
{
    pthread_t pid;
    int val = 5;
    pthread_create(&pid, NULL, threadFunc2, (void*)&val);
    pthread_join(pid, NULL);
}

3.输入fd文件描述符

void* threadFunc3(void* arg)
{
    int fd = *(int*)arg;
    cout << "child thread get fd:" << fd << endl;
    close(fd);
    pthread_exit(NULL); 
}

void test3()
{
    pthread_t pid;
    int fd = open("file", O_RDWR | O_CREAT, 0665);
    pthread_create(&pid, NULL, threadFunc3, &fd);
    pthread_join(pid, NULL);
}

4.传入char*类型

void* threadFunc4(void* arg)
{
    char* buf = (char*)arg;
    cout << "child thread is processing buf " << endl;
    buf = (char*)malloc(10 * sizeof(char));
    strcpy(buf, "hello");
    cout << "now buf is " << buf << endl;
    pthread_exit(NULL); 
}

void test4()
{
    pthread_t pid;
    char *buf = NULL;
    pthread_create(&pid, NULL, threadFunc4, buf);
    pthread_join(pid, NULL);
}

5.输入long型参数

此处传入long型参数的方式也可以按照第2种传入的方式来,更为通用

void* threadFunc5(void* arg)
{
    cout << "child thread get val:" << (long)arg << endl;
    pthread_exit(NULL); 
}

void test5()
{
    pthread_t pid;
    long val = 5;
    pthread_create(&pid, NULL, threadFunc2, (void*)val);
    pthread_join(pid, NULL);
}

6.传入结构体
传入学生的姓名,成绩,并打印

struct Student
{
    const char* name;
    double score;
};
void *threadFunc6(void* arg)
{
    Student *stu = (Student*)arg;
    for (int i = 0; i < 3; i++)
        cout << stu[i].name << ":" << stu[i].score << endl;
    pthread_exit(NULL);
}

void test6()
{
    Student stus[3] = {{"wwx", 99}, {"wk", 77}, {"wby", 88}};
    pthread_t pid;
    pthread_create(&pid, NULL, threadFunc6, stus);
    pthread_join(pid, NULL);
}

7.小练习:主线程与子线程各自加一千万,看结果是否为两千万

const int N = 10000000;
void *threadFunc7(void* arg)
{
    for (int i = 0; i < N; i++)
        *(int*)arg += 1;
    pthread_exit(NULL);
}

void test7()
{
    int sum = 0;
    pthread_t pid;
    pthread_create(&pid, NULL, threadFunc7, &sum);
    for (int i = 0; i < N; i++)
        sum ++;
    pthread_join(pid, NULL);
    cout << "final sum = " << sum << endl;
}

二.线程等待与退出 pthread_join ,pthread_exit

首先需要知道,什么情况下线程或进程会终止?
1):任意一处地方使用exit(), 进程终止;
2):main中执行到return,主线程终止, 子线程也立刻终止;
但如果是只是想要让某一个线程显式的终止,就应该调用pthread_exit函数。

再来了解 pthread_exit 线程退出函数:

  • void pthread_exit(void *__retval)

1):如果在主线程中调用了pthread_exit,那么只是主线程会终止,子线程依旧会持续进行。
2):如果在主线程中没有调用pthread_exit(), 那么主线程就最终会调用return,最终导致整个进程一起终止。

线程等待函数很好理解,用于阻塞等待指定的函数:

  • int pthread_join(pthread_t __th, void **__thread_return)

pthread_join是线程阻塞函数,第一个参数指明等待的是哪一个线程,第二个参数是接收等待线程的资源。用法与多进程的 wait() 相同。
注意: void类型是属于万金油类型,任意的数据类型都可以转换成为void类型,只需要在接收数据之后转换为原类型就可以了。

1.传出long/int型

void *threadFunc1(void *arg)
{
    long val = 4;
    pthread_exit((void *)val); //此处不能使用 (void*)&val ,因为这样传出的是地址,而不是值
}

void test1()
{
    pthread_t pid;
    pthread_create(&pid, NULL, threadFunc1, NULL);
    long val;
    pthread_join(pid, (void **)&val);
    cout << "val = " << val << endl;
}

  1. 传出char*型
void *threadFunc2(void *arg)
{
    char *s = "HelloWorld";
    pthread_exit(s);
}

void test2()
{
    pthread_t pid;
    pthread_create(&pid, NULL, threadFunc2, NULL);
    void *buf;
    pthread_join(pid, &buf);
    cout << (char *)buf << endl;
}

三.线程的取消与清理 pthread_canel , pthread_cleanup_push(pop)

  • int pthread_cancel(pthread_t __th)

取消子线程函数,此时资源必须手动清理.

  • void pthread_cleanup_push(void (*routine)(void *), void* arg)
  • void pthread_cleanup_pop(int execute)

以上为两个资源清理函数,必须成对出现。
execute值为0的情况下,只有调用pthread_exit() 或有其他线程使用pthread_cancel() 才能调用清理函数

void cleanup(void *arg)
{
    free(arg);
    cout << "thread cleanup" << endl;
}

void *threadFunc(void *arg)
{
    cout << "child thread is running " << endl;
    int *a = (int *)malloc(8);
    pthread_cleanup_push(cleanup, a);
    sleep(10);
    cout << "after sleep()" << endl;
    pthread_exit(NULL);
    pthread_cleanup_pop(0);
}

int main()
{
    pthread_t pid;
    pthread_create(&pid, NULL, threadFunc, NULL);
    sleep(1);
    pthread_cancel(pid);
    pthread_join(pid, NULL);
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值