1 创建线程
2 线程参数
3 汇合线程
4 分离线程
5 线程ID
6 终止线程
7 取消线程
1 创建线程
1.1 问题
线程就是程序的执行路线,即进程内部的控制序列,或者说是进程的子任务。一个进程可以同时拥有多个线程,即同时被系统调度的多条执行路线,但至少要有一个主线程。
IEEE POSIX 1003.1c (1995)标准,定义了统一的线程编程接口,遵循该标准的线程实现被统称为POSIX线程,即pthread。
1.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:创建线程
代码如下所示:
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void* start_routine (void* arg)
{
printf(“进入线程\n”);
sleep(10);
printf(“退出线程\n”);
returnNULL;
}
int main()
{
printf(“主线程启动\n”);
pthread_t thread;
int error = pthread_create (&thread, NULL, start_routine, NULL);
if (error)
{
printf (“pthread_create: %s\n”, strerror (error));
exit (EXIT_FAILURE);
}
sleep(15);
printf(“退出主线程\n”);
return 0;
}
上述代码中,以下代码:
void* start_routine (void* arg)
{
printf(“进入线程\n”);
sleep(10);
printf(“退出线程\n”);
return NULL;
}
定义线程过程函数start_routine。该函数有一个参数,用于从主线程传递数据。该函数中,以下代码:
sleep(10);
模拟线程需要处理的内容。该函数中,以下代码:
printf("退出线程\n");
returnNULL;
线程运行结束并退出。
上述代码中,以下代码:
pthread_t thread;
int error = pthread_create (&thread, NULL, start_routine, NULL);
if (error)
{
printf (“pthread_create: %s\n”, strerror (error));
exit (EXIT_FAILURE);
}
使用函数pthread_create创建新线程。该函数有四个参数,说明如下:
第一个参数为输出线程ID。
第二个参数为线程属性,NULL表示缺省属性。 pthread_attr_t可能是整型也可能是结构体,因实现而异。
第三个参数为线程过程函数指针。
第四个参数为传递给线程过程函数的参数。线程过程函数的调用者是系统内核,因此需要预先将参数存储到系统内核中。
pthread_create函数本身并不调用线程过程函数,而是在系统内核中开启独立的线程,并立即返回,在该线程中执行线程过程函数中的代码。
上述代码中,以下代码:
sleep(15);
模拟线程需要处理的内容。
上述代码中,以下代码:
printf(“退出主线程\n”);
return 0;
main函数可以被视为主线程的线程过程函数。main函数一旦返回,主线程即告结束。主线程一旦结束,进程即告结束。进程一旦结束,其所有的子线程统统结束。
1.3 完整代码
本案例的完整代码如下所示:
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void* start_routine (void* arg)
{
printf(“进入线程\n”);
sleep(10);
printf(“退出线程\n”);
return NULL;
}
int main()
{
printf(“主线程启动\n”);
pthread_t thread;
int error = pthread_create (&thread, NULL, start_routine, NULL);
if (error)
{
printf (“pthread_create: %s\n”, strerror (error));
exit (EXIT_FAILURE);
}
sleep(15);
printf(“退出主线程\n”);
return 0;
}
2 线程参数
2.1 问题
传递给线程过程函数的参数是一个泛型指针void*,它可以指向任何类型的数据:基本类型变量、结构体型变量或者数组型变量等等,但必须保证在线程过程函数执行期间,该指针所指向的目标变量持久有效,直到线程过程函数不再使用它为止。
调用pthread_create函数的代码在用户空间,线程过程函数的代码也在用户空间,但偏偏创建线程的动作由系统内核完成。因此传递给线程过程函数的参数也不得不经由系统内核传递给线程过程函数。pthread_create函数的arg参数负责将线程过程函数的参数带入系统内核。
2.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:线程参数
代码如下所示:
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void* start_routine (void* arg)
{
printf("%s\n", (char*)arg);
free(arg);
return NULL;
}
int main()
{
char buf = malloc(1024);
strcpy(buf, “这是主线程传递给子线程的数据”);
pthread_t thread;
int error = pthread_create (&thread, NULL, start_routine, (void)buf);
if (error)
{
printf (“pthread_create: %s\n”, strerror (error));
exit (EXIT_FAILURE);
}
sleep(1);
return 0;
}
上述代码中,以下代码:
void* start_routine (void* arg)
{
printf("%s\n", (char*)arg);
free(arg);
return NULL;
}
定义线程过程函数start_routine。该函数有一个参数,用于从主线程传递数据。该函数中,以下代码:
printf("%s\n", (char*)arg);
使用printf输出从主线程传递过来的数据。该函数中,以下代码:
free(arg);
注意数据的释放,以防止内存泄露。
上述代码中,以下代码:
char *buf = malloc(1024);
strcpy(buf, “这是主线程传递给子线程的数据”);
定义传递给子线程的数据。
上述代码中,以下代码:
pthread_t thread;
int error = pthread_create (&thread, NULL, start_routine, (void*)buf);
if (error)
{
printf (“pthread_create: %s\n”, strerror (error));
exit (EXIT_FAILURE);
}
使用函数pthread_create创建新线程。并使用第四个参数将数据传递给子线程。
上述代码中,以下代码:
sleep(1);
模拟主线程的其它操作。
2.3 完整代码
本案例的完整代码如下所示:
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void* start_routine (void* arg)
{
printf("%s\n", (char*)arg);
free(arg);
return NULL;
}
int main()
{
char buf = malloc(1024);
strcpy(buf, “这是主线程传递给子线程的数据”);
pthread_t thread;
int error = pthread_create (&thread, NULL, start_routine, (void)buf);
if (error)
{
printf (“pthread_create: %s\n”, strerror (error));
exit (EXIT_FAILURE);
}
sleep(1);
return 0;
}
3 汇合线程
3.1 问题
主线程可以等待子线程终止并与之汇合后继续运行,子线程终止后主线程将回收该线程的相关资源。
3.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:汇合线程
代码如下所示:
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void* start_routine (void* arg)
{
double r = (double)arg;
double* s = malloc (sizeof (double));
s = 3.14 * r * r;
return s;
}
int main()
{
double r = 10.0;
pthread_t thread;
int error = pthread_create (&thread, NULL, start_routine, (void)&r);
if (error)
{
perror (“pthread_create”);
exit (EXIT_FAILURE);
}
double* s;
if (pthread_join (thread, (void**)&s))
{
perror (“pthread_join”);
exit (EXIT_FAILURE);
}
printf (“圆面积:%g\n”, *s);
free (s);
return 0;
}
上述代码中,以下代码:
void* start_routine (void* arg)
{
double r = (double)arg;
double* s = malloc (sizeof (double));
*s = 3.14 * r * r;
return s;
}
定义线程过程函数start_routine。该函数有一个参数,用于从主线程传递数据。该函数中,以下代码:
double r = (double)arg;
得到主线程传过来的圆的半径。该函数中,以下代码:
double* s = malloc (sizeof (double));
*s = 3.14 * r * r;
return s;
线程过程函数将所需返回的圆的面积放在一块内存中,返回该内存的地址,同时保证这块内存,在函数返回即线程结束以后依然有效。
上述代码中,以下代码:
double r = 10.0;
定义双精度浮点型变量r作为传递给子线程的数据。
上述代码中,以下代码:
pthread_t thread;
int error = pthread_create (&thread, NULL, start_routine, (void*)&r);
if (error)
{
perror (“pthread_create”);
exit (EXIT_FAILURE);
}
使用函数pthread_create创建新线程。并使用第四个参数将数据传递给子线程。
上述代码中,以下代码:
double* s;
if (pthread_join (thread, (void**)&s))
{
perror (“pthread_join”);
exit (EXIT_FAILURE);
}
使用函数pthread_join等待线程终止并与之汇合,同时回收该线程的相关资源。该函数有两个参数,说明如下:
第一个参数为等待的线程ID。
第二个参数为输出线程过程函数的返回值。为了获得线程过程函数返回的一级泛型指针,可以同样定义一个一级泛型指针,并将其地址通过pthread_join函数的二级泛型指针参数retval传入该函数,并由该函数在thread线程终止以后,将其线程过程函数返回的一级指针填入该二级指针的目标。
上述代码中,以下代码:
printf ("圆面积:%g\n", *s);
在主线程中,输出圆的面积。
上述代码中,以下代码:
free (s);
不要忘记释放子线程传回的数据所占的内存。
3.3 完整代码
本案例的完整代码如下所示:
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void* start_routine (void* arg)
{
double r = (double)arg;
double* s = malloc (sizeof (double));
s = 3.14 * r * r;
return s;
}
int main()
{
double r = 10.0;
pthread_t thread;
int error = pthread_create (&thread, NULL, start_routine, (void)&r);
if (error)
{
perror (“pthread_create”);
exit (EXIT_FAILURE);
}
double* s;
if (pthread_join (thread, (void**)&s))
{
perror (“pthread_join”);
exit (EXIT_FAILURE);
}
printf (“圆面积:%g\n”, *s);
free (s);
return 0;
}
4 分离线程
4.1 问题
信号量与其它几种IPC机制(管道、共享内存和消息队列)都不一样,它的目的不是在进程之间搭建数据流通的桥梁,而是提供一个可为多个进程共同访问计数器,实时跟踪可用资源的数量,以解决多个用户分享有限资源时的竞争与冲突问题
4.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:分离线程
代码如下所示:
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void* start_routine (void* arg)
{
pthread_detach (pthread_self ());
printf("%s\n", (char*)arg);
free(arg);
return NULL;
}
int main()
{
char buf = malloc(1024);
strcpy(buf, “这是主线程传递给子线程的数据”);
pthread_t thread;
int error = pthread_create (&thread, NULL, start_routine, (void)buf);
if (error)
{
printf (“pthread_create: %s\n”, strerror (error));
exit (EXIT_FAILURE);
}
sleep(1);
return 0;
}
上述代码中,以下代码:
void* start_routine (void* arg)
{
pthread_detach (pthread_self ());
printf("%s\n", (char*)arg);
free(arg);
return NULL;
}
定义线程过程函数start_routine。该函数有一个参数,用于从主线程传递数据。该函数中,以下代码:
pthread_detach (pthread_self ());
使用函数pthread_detach使指定线程进入分离状态,其中参数为另一个函数调用pthread_self (),该调用将获取子线程的ID。pthread_detach函数使子线程进入分离状态(DETACHED)。处于分离状态的线程一旦终止,其线程资源即被系统自动回收。处于分离状态的线程不能被pthread_join函数汇合。
上述代码中,以下代码:
char buf = malloc(1024);
strcpy(buf, “这是主线程传递给子线程的数据”);
pthread_t thread;
int error = pthread_create (&thread, NULL, start_routine, (void)buf);
if (error)
{
printf (“pthread_create: %s\n”, strerror (error));
exit (EXIT_FAILURE);
}
首先,在主线程中定义传给子线程的数据。
然后,使用pthread_create创建子线程,并使用第四个参数将数据传给子线程。
4.3 完整代码
本案例的完整代码如下所示:
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void* start_routine (void* arg)
{
pthread_detach (pthread_self ());
printf("%s\n", (char*)arg);
free(arg);
return NULL;
}
int main()
{
char buf = malloc(1024);
strcpy(buf, “这是主线程传递给子线程的数据”);
pthread_t thread;
int error = pthread_create (&thread, NULL, start_routine, (void)buf);
if (error)
{
printf (“pthread_create: %s\n”, strerror (error));
exit (EXIT_FAILURE);
}
sleep(1);
return 0;
}
5 线程ID
5.1 问题
在任何线程中均可以获取其自身的线程ID,还可以判断两个线程ID是否相同。
5.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:线程ID
代码如下所示:
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void* routine1 (void* arg)
{
pthread_t thread = pthread_self ();
printf (“子线程ID:%ul\n”, thread);
return thread;
}
void routine2 (void* arg)
{
pthread_t thread = pthread_self ();
printf (“子线程ID:%ul\n”, thread);
if (pthread_equal (thread, (pthread_t)arg))
printf (“两个线程ID相同\n”);
else
printf (“两个线程ID不同\n”);
return thread;
}
int main()
{
pthread_t thread1;
int error = pthread_create (&thread1, NULL, routine1, NULL);
if (error)
{
perror (“pthread_create”);
exit (EXIT_FAILURE);
}
pthread_t thread2;
error = pthread_create (&thread2, NULL, routine2, (void)&thread1);
if (error)
{
perror (“pthread_create”);
exit (EXIT_FAILURE);
}
sleep(1);
return 0;
}
上述代码中,以下代码:
void* routine1 (void* arg)
{
pthread_t thread = pthread_self ();
printf (“子线程ID:%ul\n”, *thread);
return thread;
}
定义线程过程函数routine1。该函数有一个参数,用于从主线程传递数据。该函数中,以下代码:
pthread_t thread = pthread_self ();
printf ("子线程ID:%ul\n", *thread);
使用函数pthread_self获取当前线程的ID,并输出。
上述代码中,以下代码:
void* routine2 (void* arg)
{
pthread_t thread = pthread_self ();
printf (“子线程ID:%ul\n”, *thread);
if (pthread_equal (thread, (pthread_t)arg))
printf (“两个线程ID相同\n”);
else
printf (“两个线程ID不同\n”);
return thread;
}
定义线程过程函数routine2。该函数有一个参数,用于从主线程传递数据。该函数中,以下代码:
pthread_t thread = pthread_self ();
printf ("子线程ID:%ul\n", *thread);
使用函数pthread_self获取当前线程的ID,并输出。该函数中,以下代码:
if (pthread_equal (thread, (pthread_t)arg))
printf (“两个线程ID相同\n”);
else
printf (“两个线程ID不同\n”);
使用函数pthread_equal判断两个线程ID是否相等,该函数的两个参数分别为两个线程的ID号。
上述代码中,以下代码:
pthread_t thread1;
int error = pthread_create (&thread1, NULL, routine1, NULL);
if (error)
{
perror (“pthread_create”);
exit (EXIT_FAILURE);
}
使用函数pthread_create创建新线程。
上述代码中,以下代码:
pthread_t thread2;
error = pthread_create (&thread2, NULL, routine2, (void*)&thread1);
if (error)
{
perror (“pthread_create”);
exit (EXIT_FAILURE);
}
使用函数pthread_create再次创建新线程。并使用第四个参数将前面创建的线程ID作为参数传给当前创建的线程。
5.3 完整代码
本案例的完整代码如下所示:
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void* routine1 (void* arg)
{
pthread_t thread = pthread_self ();
printf (“子线程ID:%ul\n”, thread);
return thread;
}
void routine2 (void* arg)
{
pthread_t thread = pthread_self ();
printf (“子线程ID:%ul\n”, thread);
if (pthread_equal (thread, (pthread_t)arg))
printf (“两个线程ID相同\n”);
else
printf (“两个线程ID不同\n”);
return thread;
}
int main()
{
pthread_t thread1;
int error = pthread_create (&thread1, NULL, routine1, NULL);
if (error)
{
perror (“pthread_create”);
exit (EXIT_FAILURE);
}
pthread_t thread2;
error = pthread_create (&thread2, NULL, routine2, (void)&thread1);
if (error)
{
perror (“pthread_create”);
exit (EXIT_FAILURE);
}
sleep(1);
return 0;
}
6 终止线程
6.1 问题
在线程过程函数或者被线程过程函数直接或间接调用的函数中,调用pthread_exit函数,其效果都与在线程过程函数中执行return语句的效果一样——终止调用线程。
注意,在任何线程中调用exit函数,被终止的都是进程。当然随着进程的终止,隶属于该进程的包括调用线程在内的所有线程也都一并终止。
6.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:终止线程
代码如下所示:
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void circle_area (double r)
{
double* s = malloc (sizeof (double));
s = 3.14 * r * r;
pthread_exit (s);
}
void start_routine (void* arg)
{
circle_area ((double)arg);
printf(“此句将不被执行”);
return NULL;
}
int main()
{
double r = 10.0;
pthread_t thread;
int error = pthread_create (&thread, NULL, start_routine, (void*)&r);
if (error)
{
perror (“pthread_create”);
exit (EXIT_FAILURE);
}
double* s;
if (pthread_join (thread, (void**)&s))
{
perror (“pthread_join”);
exit (EXIT_FAILURE);
}
printf (“圆面积:%g\n”, *s);
free (s);
return 0;
}
上述代码中, 以下代码:
void circle_area (double r)
{
double* s = malloc (sizeof (double));
*s = 3.14 * r * r;
pthread_exit (s);
}
定义了一个普通函数circle_area,用于求圆的面积,该函数的参数是圆的半径。该函数中,以下代码:
pthread_exit (s);
使用函数pthread_exit令当前线程终止运行,函数的参数为线程的返回值。
上述代码中, 以下代码:
void* start_routine (void* arg)
{
circle_area ((double)arg);
printf(“此句将不被执行”);
return NULL;
}
定义线程过程函数start_routine。该函数有一个参数,用于从主线程传递数据。该函数中,以下代码:
circle_area (*(double*)arg);
调用函数circle_area计算圆的面积。该函数中,以下代码:
printf("此句将不被执行");
return NULL;
将不会被执行,因为在计算圆的面积函数circle_area中调用了pthread_exit函数终止了当前线程。
上述代码中, 以下代码:
double r = 10.0;
pthread_t thread;
int error = pthread_create (&thread, NULL, start_routine, (void*)&r);
if (error)
{
perror (“pthread_create”);
exit (EXIT_FAILURE);
}
使用函数pthread_create创建新线程。并使用第四个参数将圆的半径r传给当前创建的线程。
上述代码中, 以下代码:
double* s;
if (pthread_join (thread, (void**)&s))
{
perror (“pthread_join”);
exit (EXIT_FAILURE);
}
printf (“圆面积:%g\n”, *s);
free (s);
等待线程结束,再继续执行主线程,打印圆的面积。
6.3 完整代码
本案例的完整代码如下所示:
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void circle_area (double r)
{
double* s = malloc (sizeof (double));
s = 3.14 * r * r;
pthread_exit (s);
}
void start_routine (void* arg)
{
circle_area ((double)arg);
printf(“此句将不被执行”);
return NULL;
}
int main()
{
double r = 10.0;
pthread_t thread;
int error = pthread_create (&thread, NULL, start_routine, (void*)&r);
if (error)
{
perror (“pthread_create”);
exit (EXIT_FAILURE);
}
double* s;
if (pthread_join (thread, (void**)&s))
{
perror (“pthread_join”);
exit (EXIT_FAILURE);
}
printf (“圆面积:%g\n”, *s);
free (s);
return 0;
}
7 取消线程
7.1 问题
在线程过程函数或者被线程过程函数直接或间接调用的函数中,调用pthread_exit函数,其效果都与在线程过程函数中执行return语句的效果一样——终止调用线程。
注意,在任何线程中调用exit函数,被终止的都是进程。当然随着进程的终止,隶属于该进程的包括调用线程在内的所有线程也都一并终止。
7.2 步骤
实现此案例需要按照如下步骤进行。
步骤一:取消线程
代码如下所示:
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void* start_routine (void* arg)
{
printf(“子线程开始\n”);
while(1)
{
printf(“子线程运行中…\n”);
sleep(1);
}
printf(“子线程退出\n”);
pthread_exit(NULL);
}
int main()
{
pthread_t thread;
int error = pthread_create (&thread, NULL, start_routine, NULL);
if (error)
{
perror (“pthread_create”);
exit (EXIT_FAILURE);
}
sleep(5);
if (pthread_cancel (thread))
{
perror (“pthread_cancel”);
exit (EXIT_FAILURE);
}
if (pthread_join (thread, NULL))
{
perror (“pthread_join”);
exit (EXIT_FAILURE);
}
printf(“主线程退出\n”);
return 0;
}
上述代码中,以下代码:
void* start_routine (void* arg)
{
printf(“子线程开始\n”);
while(1)
{
printf(“子线程运行中…\n”);
sleep(1);
}
printf(“子线程退出\n”);
pthread_exit(NULL);
}
定义线程过程函数start_routine。该函数有一个参数,用于从主线程传递数据。该函数中,以下代码:
printf("子线程开始\n");
输出子线程开始,标志着子线程开始运行。该函数中,以下代码:
while(1)
{
printf(“子线程运行中…\n”);
sleep(1);
}
设置一个死循环,每隔一秒输出提示子线程运行中。该函数中,以下代码:
printf("子线程退出\n");
pthread_exit(NULL);
输出提示子线程退出,并退出子线程。
上述代码中,以下代码:
pthread_t thread;
int error = pthread_create (&thread, NULL, start_routine, NULL);
if (error)
{
perror (“pthread_create”);
exit (EXIT_FAILURE);
}
使用函数pthread_create创建新线程。
上述代码中,以下代码:
sleep(5);
模拟主线程的其它操作。
上述代码中,以下代码:
if (pthread_cancel (thread))
{
perror (“pthread_cancel”);
exit (EXIT_FAILURE);
}
使用函数pthread_cancel向指定线程发送取消请求,该函数的参数是指定线程的ID。缺省情况下,线程在收到取消请求以后,并不会立即终止,而是仍继续运行,直到其达到某个取消点。在取消点处,线程检查其自身是否已被取消,若是则立即终止。当子线程中调用函数sleep时,取消点会出现。
上述代码中,以下代码:
if (pthread_join (thread, NULL))
{
perror (“pthread_join”);
exit (EXIT_FAILURE);
}
printf(“主线程退出\n”);
等待线程结束,再继续执行主线程,输出提示主线程退出。
7.3 完整代码
本案例的完整代码如下所示:
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void* start_routine (void* arg)
{
printf(“子线程开始\n”);
while(1)
{
printf(“子线程运行中…\n”);
sleep(1);
}
printf(“子线程退出\n”);
pthread_exit(NULL);
}
int main()
{
pthread_t thread;
int error = pthread_create (&thread, NULL, start_routine, NULL);
if (error)
{
perror (“pthread_create”);
exit (EXIT_FAILURE);
}
sleep(5);
if (pthread_cancel (thread))
{
perror (“pthread_cancel”);
exit (EXIT_FAILURE);
}
if (pthread_join (thread, NULL))
{
perror (“pthread_join”);
exit (EXIT_FAILURE);
}
printf(“主线程退出\n”);
return 0;
}