0、前言
本文是分享在学习多线程编程过程中的知识点,主要是面对刚入门多线程编程的同学。本文将通过两个简单的例子来逐一说明在多线程编程时的要点以及一些细节问题,需要各位同学有那么一丢丢的linux和c语言的基础,不过没有也没关系,本文会将所有需要的命令展示出来,以保证大家都能够在自己的电脑上操作起来,并能够运行得到结果。(由于本人也在学习中,有不对的地方请多多指正)
1、线程介绍
线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程中,是进程中的实际运作单位。在Linux中,线程的本质仍然是进程。线程有PCB,但没有独立的地址空间,多个子线程共享同一个进程空间。关于进程的概念,将在以后的文章说明。
对于一般的程序而言,可分为主线程和子线程,子线程和主线程共享同一个PID,在linux中可用gitpid()函数查看。那么主线程和子线程到底谁先执行呢?答案是不一定,谁先抢到CPU的时间片,谁就先执行。关于线程的概念大家可以找其他教程进行学习。
2、创建线程
2.1 phread_create函数
phtread_cteate的函数原型为:int pthread_cteate(pthread_t thread,const pthread_attr_t *attr,
void* (*start_routine)(void*),void *arg);
在linux中,函数返回值一般有两种,成功返回0,失败则返回错误号。
下面对phtread_cteate函数中的参数逐一解释:
(1) phtread_t:用于保存创建出来的线程ID,可以用thread_self()函数查看。
(2) attr:一般传递NULL,表示使用线程的默认属性。
(3) start_routine:函数指针,表示在子线程中要调用哪个函数,如果函数调用结束,则该线程结束。
(4) arg:给线程中的函数传递的参数,可以是一个或者多个。
注意事项:
(1) 由于pthread_create的错误码不保存在errno中,因此不能直接用perror()打印错误信息,可以先用strerror()把错误码转换成错误信息再打印。
(2) 如果任意一个线程调用了exit或_exit,则整个进程的所有线程都终止,由于从main函数return也相当于调用exit,为了防止新创建的线程还没有得到执行就终止,我们在main函数return之前延时1秒,这只是一种权宜之计,即使主线程等待1秒,内核也不一定会调度新创建的线程执行。以后将会利用 pthread_join()。
2.2 pthread_create实例
首先在linux终端中输入”vim pthread_cteate.c“ 创建相应的代码文件,然后在文件里面输入以下内容,在vim里面粘贴的快捷键为shift+ctrl+v。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
//创建子线程
//线程执行函数
void *mythread(void *arg)
{
//打印子线程的pid和线程号(主线程和子线程pid相同,线程号不同)
printf("child thread, pid==[%d], id==[%ld]\n", getpid(), pthread_self());
}
int main()
{
//int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
// void *(*start_routine) (void *), void *arg);
//创建子线程
pthread_t thread;
int ret = pthread_create(&thread, NULL, mythread, NULL);
//创建了子线程,同时争夺cpu的时间片,谁先得到,谁就先运行。
//如果ret为0,则创建子线程成功,若不成功,则返回响应的错误号
if(ret!=0)
{
//创建子线程失败
printf("pthread_create error, [%s]\n", strerror(ret));
return -1;
}
//打印主线程的pid以及线程号
printf("main thread, pid==[%d], id==[%ld]\n", getpid(), pthread_self());
//让主线程休眠1s 目的是提高子线程先获得时间片的概率,避免主线程结束了,而子线程还没开始运行。
sleep(1);
return 0;
}
退出文件,在终端输入 gcc pthread_create.c -lpthread,也可以用 gcc -o a.out pthread_create.c -lpthread,此时在文件下面会产生一个a.out的二进制文件,利用ls命令查看,再输入:./a.out就可以执行了。从结果可以看出,主线程和子线程的pid一致,但是线程号不同。
3、pthread_cteate传递参数
在上面的例子中,子进程的函数中并没有得到参数,而在实际过程中,我们需要给子进程传递某些参数,这就要利用*arg这个参数了。下面是给pthread_cteate传递参数的例子。
//创建子线程: 传递参数
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
//定义一个结构体
struct Test
{
int data;
char name[64];
};
//线程执行函数
void *mythread(void *arg)
{
//int n = *(int *)arg;
//接受的是void*万能指针,这里将其转化为结构体Test的指针
struct Test *p = (struct Test *)arg;
//struct Test *p = arg;
//printf("n==[%d]\n", n);
printf("[%d][%s]\n", p->data, p->name);
printf("child thread, pid==[%d], id==[%ld]\n", getpid(), pthread_self());
}
int main()
{
int n = 99;
struct Test t;
//memeset的作用是初始化内存,将内存中的值全部用16进制的0x00代替
memset(&t, 0x00, sizeof(struct Test));
t.data = 88;
//把"xiaowen"复制给t.name
strcpy(t.name, "xiaowen");
//int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
// void *(*start_routine) (void *), void *arg);
//创建子线程
pthread_t thread;
//int ret = pthread_create(&thread, NULL, mythread, &n);
int ret = pthread_create(&thread, NULL, mythread, &t);
if(ret!=0)
{
printf("pthread_create error, [%s]\n", strerror(ret));
return -1;
}
printf("main thread, pid==[%d], id==[%ld]\n", getpid(), pthread_self());
//目的是为了让子线程能够执行起来
sleep(1);
return 0;
}
4、总结
本文介绍了进程的概念,通过两个例子介绍了怎么创建子线程,有了这个概念之后,下一步将进一步对多线程编程进行学习,希望小伙伴们多多支持。