线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属于一个进程的其他的线程共享进程拥有的全部资源。
Linux进程创建一个新线程时,线程将拥有自己的栈(因为线程有自己的局部变量),但与它的创建者共享全局变量、文件描述符、信号句柄和当前目录状态。
Linux通过fork创建子进程与创建线程之间是有区别的:fork创建出该进程的一份拷贝,这个新进程拥有自己的变量和自己的PID,它的时间调度是独立的,它的执行几乎完全独立于父进程。
进程可以看成一个资源的基本单位,而线程是程序调度的基本单位,一个进程内部的线程之间共享进程获得的时间
1、线程的创建
#include <pthread.h>
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 *)型的返回值,它代表线程的退出状态。
#include <stdio.h>
#include <pthread.h>
void *thread_run(void* a)
{
while (1)
{
sleep(1);
printf ("线程 函数\n");
}
}
int main()
{
pthread_t thread_id;
int ret = pthread_create(&thread_id, NULL, thread_run, NULL);
if (ret != 0)
{
perror ("pthread_create");
return -1;
}
while (1)
{
sleep(1);
printf ("main 函数\n");
}
return 0;
}
2、线程的参数传递
#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, thread_run, (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);
printf ("result = %d\n", *result);
return 0;
}
3、线程ID的获取
include <pthread.h>
pthread_t pthread_self(void);
返回当前线程的ID
可以使用pthread_equal()函数检查两个线程ID是否相同
include <pthread.h>
int pthread_equal(pthread_t t1, pthread_t t2);
如果想相等则返回0
4、线程终止
#include <pthread.h>
void pthread_exit(void *retval);
参数说明:
retval:返回指针,指向线程向要返回的某个对象。
线程通过调用pthread_exit函数终止执行,并返回一个指向某对象的指针。注意:绝不能用它返回一个指向局部变量的指针,因为线程调用结束后,这个局部变量就不存在了,这将引起严重的程序漏洞。
调用pthread_exit()等价于在工作函数中执行return,区别是pthread_exit()可以在工作函数调用的任何函数中被调用。
如果主线程调用pthread_exit(),而不是exit()或执行return,则其他线程将继续执行。
5、线程等待和线程分离
#include <pthread.h>
int pthread_join(pthread_t th, void **thread_return);
参数说明:
th:将要等待的线程,线程通过pthread_create返回的标识符来指定。
thread_return:一个指针,指向另一个指针,而后者指向线程的返回值。不需要返回值则为NULL。
#include <pthread.h>
int pthread_detach(pthread_t thread);
成功返回0。失败返回错误值
一个线程可以使用下面的方式分离自己:
pthread_detach(pthread_self());
pthread_detach()不会导致调用者阻塞,也不会导致所操作的线程结束。如果调用pthread_detach()时线程已经结束,则清理其所占用的资源。
对于创建时处于未分离状态的线程,必须调用一次pthread_join()或pthread_detach(),否则线程结束后就会留下没有释放的资源。