前言:你需要知道多线程可以公用全局变量直接进行通信;Linux进程创建一个新线程时,线程将拥有自己的栈(因为线程有自己的局部变量),但与它的创建者共享全局变量、文件描述符、信号句柄和当前目录状态。
1、概念
线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属于一个进程的其他的线程共享进程拥有的全部资源。
2、和进程比较
(1)、和进程相比,它是一种非常"节俭"的多任务操作方式。在linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种"昂贵"的多任务工作方式。
(2)、运行于一个进程中的多个线程,它们之间使用相同的地址空间,而且线程间彼此切换所需时间也远远小于进程间切换所需要的时间。据统计,一个进程的开销大约是一个线程开销的30倍左右。
(3)、线程间方便的通信机制。对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过进程间通信的方式进行,这种方式不仅费时,而且很不方便。线程则不然,由于同一进城下的线程之间贡献数据空间,所以一个线程的数据可以直接为其他线程所用,这不仅快捷,而且方便。
3、线程创建
int pthread_create(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
参数说明:
thread:指向pthread_t类型的指针,用于引用新创建的线程。
attr:用于设置线程的属性,一般不需要特殊的属性,所以可以简单地设置为NULL。
*(*start_routine)(void *):传递新线程所要执行的函数地址。
arg:新线程所要执行的函数的参数。
调用如果成功,则返回值是0,如果失败则返回错误代码。
与创建进程不同,创建线程时可以指定一个工作函数,新线程将从这个函数开始执行,函数返回也就等价于线程退出。
工作函数必须有一个(void *)型参数,新线程开始执行时,这个参数的值就是pthread_create函数的arg参数的值,因此可以利用它来向线程传递数据。
工作函数必须有(void *)型的返回值,它代表线程的退出状态。
4、线程终止
void pthread_exit(void *retval)
注意:这只是线程的终止;而不是退出,终止是指不往下执行,退出就是直接退出程序,结束了程序;
参数说明:
retval:返回指针,指向线程向要返回的某个对象。
线程通过调用pthread_exit函数终止执行,并返回一个指向某对象的指针。注意:绝不能用它返回一个指向局部变量的指针,因为线程调用结束后,这个局部变量就不存在了,这将引起严重的程序漏洞。
调用pthread_exit()等价于在工作函数中执行return,区别是pthread_exit()可以在工作函数调用的任何函数中被调用。
如果主线程调用pthread_exit(),而不是exit()或执行return,则其他线程将继续执行。
5、等待线程结束
int pthread_join(pthread_t th, void **thread_return);
和
int pthread_detach(pthread_t thread);
pthread_detach()不会导致调用者阻塞,也不会导致所操作的线程结束。如果调用pthread_detach()时线程已经结束,则清理其所占用的资源,而pthread_join是阻塞的
对于创建时处于未分离状态的线程,必须调用一次pthread_join()或pthread_detach(),否则线程结束后就会留下没有释放的资源。
注意点:
(1)除了局部变量以外,所有其他变量都将在一个进程中的所有线程之间共享。
(2)线程没有像进程那样的父子关系,仅有属于同一个进程的“同组”关系(进程实际上代表的是一个线程组)。
(3) 在POSIX线程模型中,主线程可以创建一个新线程A,新线程A又可以创建另一个新线程B,线程A和B本身没有父子关系,只是同属于一个进程。
(4)等待线程结束的pthread_join()操作可以由任何一个同组的线程发起,不必是主线程。另外,如果主线程退出,即进程退出,则所有的线程也会随之退出。
6、代码实例
#include <stdio.h>
#include <pthread.h>
struct data //多线程可以公用全局变量;
{
int a;
int b;
};
void *thread_run(void* a)
{
struct data *value = (struct data*)a;
printf ("线程接收到参数: %d, %d\n", value->a, value->b);
int ret = value->a + value->b;
// 不能通过 pthread_exit 返回栈上变量的地址,也就是局部变量指针;
pthread_exit((void*)ret); //终止线程返回结果;
// pthread_exit((void*)&ret);
}
pthread_t func()
{
struct data value;
value.a = 10;
value.b = 20;
pthread_t thread_id;
// 给线程传参要注意 栈上的变量的生命周期 //全局变量是在数据域,局部变量还有函数是在栈上
int ret = pthread_create(&thread_id, NULL, , (void *)&value);
if (ret != 0)
{
perror ("pthread_create");
return -1;
}
return thread_id;
}
int main()
{
int *result; //用指针来接线程返回的结果
pthread_t thread_id = func();
printf ("等待线程结束\n");
pthread_join(thread_id, (void **)&result); //等待thread_id线程,第二个参数接的是线程的返回值;
printf ("result = %d\n", *result);
return 0;
}