-
要想使用POSIX标准线程API(pthreads),需要连接libpthread.so库到程序中。
1、创建线程
-
进程中的每个线程都有一个线程号,类型为pthread_t。
-
用pthread_self函数可以返回当前线程的线程号。
-
线程号之间的比较可以用函数pthread_equal。
if (!pthread_equal (pthread_self (), other_thread))
pthread_join (other_thread, NULL);
|
-
用函数pthread_create函数可以创建一个线程:
-
第一个参数是一个pthread_t类型的变量的指针,用于存储线程号。
-
第二个参数是一个pthread_attr_t类型的线程属性对象,NULL表示使用默认属性
-
第三个参数是一个指向线程函数的指针
-
第四个参数是void指针,指向传给线程的参数
-
编译并链接程序:
% cc -o thread-create thread-create.c –lpthread
|
-
线程退出有两种方式:
-
线程函数退出。线程函数的返回值就是线程的返回值。
-
显式调用pthread_exit,其参数为线程的返回值。
#include <pthread.h>
#include <stdio.h>
/* Prints x’s to stderr. The parameter is unused. Does not return. */
void* print_xs (void* unused)
{
while (1)
fputc (‘x’, stderr);
return NULL;
}
/* The main program. */
int main ()
{
pthread_t thread_id;
/* Create a new thread. The new thread will run the print_xs function. */
pthread_create (&thread_id, NULL, &print_xs, NULL);
/* Print o’s continuously to stderr. */
while (1)
fputc (‘o’, stderr);
return 0;
}
|
1.1、向线程传递数据
Listing 4.2 (thread-create2) Create Two Threads
#include <pthread.h>
#include <stdio.h>
/* Parameters to print_function. */
struct char_print_parms
{
/* The character to print. */
char character;
/* The number of times to print it. */
int count;
};
/* Prints a number of characters to stderr, as given by PARAMETERS,
which is a pointer to a struct char_print_parms. */
void* char_print (void* parameters)
{
/* Cast the cookie pointer to the right type. */
struct char_print_parms* p = (struct char_print_parms*) parameters;
int i;
for (i = 0; i < p->count; ++i)
fputc (p->character, stderr);
return NULL;
}
/* The main program. */
int main ()
{
pthread_t thread1_id;
pthread_t thread2_id;
struct char_print_parms thread1_args;
struct char_print_parms thread2_args;
/* Create a new thread to print 30,000 ’x’s. */
thread1_args.character = ’x’;
thread1_args.count = 30000;
pthread_create (&thread1_id, NULL, &char_print, &thread1_args);
/* Create a new thread to print 20,000 o’s. */
thread2_args.character = ’o’;
thread2_args.count = 20000;
pthread_create (&thread2_id, NULL, &char_print, &thread2_args);
return 0;
}
|
此程序有一个Bug,主线程创建线程的时候,穿进去的参数是局部变量,如果主线程在从线程之前退出,则局部变量会被释放,然而从线程却可能仍然要访问这些变量,就会出错。因而应该应用join使得主线程等待从线程结束。
1.2、Join线程
-
phread_join有两个参数:
-
第一个参数是要join的线程号。
-
第二个参数是一个void指针,指向一个变量来接收线程的返回值。
Revised Main Function for thread-create2.c
int main ()
{
pthread_t thread1_id;
pthread_t thread2_id;
struct char_print_parms thread1_args;
struct char_print_parms thread2_args;
/* Create a new thread to print 30,000 x’s. */
thread1_args.character = ’x’;
thread1_args.count = 30000;
pthread_create (&thread1_id, NULL, &char_print, &thread1_args);
/* Create a new thread to print 20,000 o’s. */
thread2_args.character = ’o’;
thread2_args.count = 20000;
pthread_create (&thread2_id, NULL, &char_print, &thread2_args);
/* Make sure the first thread has finished. */
pthread_join (thread1_id, NULL);
/* Make sure the second thread has finished. */
pthread_join (thread2_id, NULL);
/* Now we can safely return. */
return 0;
}
|
1.3、线程返回值
用线程计算素数
#include <pthread.h>
#include <stdio.h>
/* Compute successive prime numbers (very inefficiently). Return the
Nth prime number, where N is the value pointed to by *ARG. */
void* compute_prime (void* arg)
{
int candidate = 2;
int n = *((int*) arg);
while (1) {
int factor;
int is_prime = 1;
/* Test primality by successive division. */
for (factor = 2; factor < candidate; ++factor)
if (candidate % factor == 0) {
is_prime = 0;
break;
}
/* Is this the prime number we’re looking for? */
if (is_prime) {
if (--n == 0)
/* Return the desired prime number as the thread return value. */
return (void*) candidate;
}
++candidate;
}
return NULL;
}
int main ()
{
pthread_t thread;
int which_prime = 5000;
int prime;
/* Start the computing thread, up to the 5,000th prime number. */
pthread_create (&thread, NULL, &compute_prime, &which_prime);
/* Do some other work here... */
/* Wait for the prime number thread to complete, and get the result. */
pthread_join (thread, (void*) &prime);
/* Print the largest prime it computed. */
printf(“The %dth prime number is %d.\n”, which_prime, prime);
return 0;
}
|
1.4、线程属性
-
使用线程属性:
-
创建pthread_attr_t对象。
-
调用pthread_attr_init来初始化为默认属性。
-
可以修改线程属性对象,设置需要的属性值。
-
调用pthread_create的时候将属性对象指针传入。
-
调用pthread_attr_destroy来释放属性对象。
-
pthread_attr_t本身不能自动回收,但是可以用pthread_attr_init函数重新初始化线程对象。
-
使用detach state属性
-
一个线程可以默认状态下被创建称为可join的线程,也可以创建为detached线程。
-
一个可join的线程在结束后不会自动被回收,而是要其他线程调用pthread_join来获得此线程的返回值。
-
一个detached线程在结束后会自动被回收。
-
用函数pthread_attr_setdetachstate来设定线程属性对象的detach state.
#include <pthread.h>
void* thread_function (void* thread_arg)
{
/* Do work here... */
}
int main ()
{
pthread_attr_t attr;
pthread_t thread;
pthread_attr_init (&attr);
pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
pthread_create (&thread, &attr, &thread_function, NULL);
pthread_attr_destroy (&attr);
/* Do work here... */
/* No need to join the second thread. */
return 0;
}
|
2、取消线程
-
一个线程可以调用pthread_cancel来取消另一个线程。
-
被取消的线程需要被join来释放资源。
-
被取消的线程的返回值为PTHREAD_CANCELED
-
有关线程的取消,一个线程可以为如下三个状态:
-
可异步取消:一个线程可以在任何时刻被取消。
-
可同步取消:取消的请求被放在队列中,直到线程到达某个点,才被取消。
-
不可取消:取消的请求被忽略。
-
默认状态下,线程是可同步取消的。
-
调用pthread_setcanceltype来设定线程取消的方式:
-
pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
-
pthread_setcanceltype (PTHREAD_CANCEL_DEFERRED, NULL);
-
pthread_setcanceltype (PTHREAD_CANCEL_DISABLE, NULL);
3、线程私有数据
-
每个线程私有数据都有一个key
-
每个线程都使用此key来访问属于它的那份数据。
-
调用pthread_key_create来为此线程创建一个新的key和数据。
-
第一个参数是pthread_key_t
-
第二个参数是一个回收函数,会在线程退出的时候被调用。
-
每个线程可以调用pthread_setspecific,根据key来设定线程私有数据的值。
-
调用pthread_getspecific来得到一个线程私有数据。
#include <malloc.h>
#include <pthread.h>
#include <stdio.h>
/* The key used to associate a log file pointer with each thread. */
static pthread_key_t thread_log_key;
/* Write MESSAGE to the log file for the current thread. */
void write_to_thread_log (const char* message)
{
FILE* thread_log = (FILE*) pthread_getspecific (thread_log_key);
fprintf (thread_log, “%s\n”, message);
}
/* Close the log file pointer THREAD_LOG. */
void close_thread_log (void* thread_log)
{
fclose ((FILE*) thread_log);
}
void* thread_function (void* args)
{
char thread_log_filename[20];
FILE* thread_log;
/* Generate the filename for this thread’s log file. */
sprintf (thread_log_filename, “thread%d.log”, (int) pthread_self ());
/* Open the log file. */
thread_log = fopen (thread_log_filename, “w”);
/* Store the file pointer in thread-specific data under thread_log_key. */
pthread_setspecific (thread_log_key, thread_log);
write_to_thread_log (“Thread starting.”);
/* Do work here... */
return NULL;
}
int main ()
{
int i;
pthread_t threads[5];
/* Create a key to associate thread log file pointers in thread-specific data. Use close_thread_log to clean up the file
pointers. */
pthread_key_create (&thread_log_key, close_thread_log);
/* Create threads to do the work. */
for (i = 0; i < 5; ++i)
pthread_create (&(threads[i]), NULL, thread_function, NULL);
/* Wait for all threads to finish. */
for (i = 0; i < 5; ++i)
pthread_join (threads[i], NULL);
return 0;
}
|
3.1、线程回收
Linux可以提供回收器(cleanup handler),它是一个函数,在线程退出的时候被调用。
调用pthread_cleanup_push可以注册一个回收器。
调用pthread_cleanup_pop可以注销一个回收器。
pthread_cleanup_pop(0)仅仅注销一个回收器。
pthread_cleanup_pop(1)不仅仅注销这个回收器,而且调用它。
#include <malloc.h>
#include <pthread.h>
/* Allocate a temporary buffer. */
void* allocate_buffer (size_t size)
{
return malloc (size);
}
/* Deallocate a temporary buffer. */
void deallocate_buffer (void* buffer)
{
free (buffer);
}
void do_some_work ()
{
/* Allocate a temporary buffer. */
void* temp_buffer = allocate_buffer (1024);
/* Register a cleanup handler for this buffer, to deallocate it in case the thread exits or is cancelled. */
pthread_cleanup_push (deallocate_buffer, temp_buffer);
/* Do some work here that might call pthread_exit or might be cancelled... */
/* Unregister the cleanup handler. Because we pass a nonzero value, this actually performs the cleanup by calling
deallocate_buffer. */
pthread_cleanup_pop (1);
}
|
4、线程同步及临界区
4.1、互斥锁(Mutexes)
-
Mutex全称MUTual EXclusion lock,也即互斥锁。
-
同一时刻,只有一个线程可以访问互斥锁。
-
调用pthread_mutex_init可以创建互斥锁:
-
第一个参数是pthread_mutex_t
-
第二个参数是互斥锁属性对象,设为NULL表示使用默认属性。
pthread_mutex_t mutex;
pthread_mutex_init (&mutex, NULL);
|
-
创建互斥锁的第二种方式:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
-
调用pthread_mutex_lock来锁定互斥锁。
-
调用pthread_mutex_unlock来解锁互斥锁。
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <pthread.h>
struct job
{
struct job* next;
int value;
};
struct job* job_queue;
void process_job(struct job*);
pthread_mutex_t job_queue_mutex = PTHREAD_MUTEX_INITIALIZER;
void * process_queue_function (void * arg)
{
while(1)
{
struct job* next_job;
pthread_mutex_lock(&job_queue_mutex);
if(job_queue == NULL)
next_job = NULL;
else
{
printf("begin removing a job...\n");
next_job = job_queue;
job_queue = job_queue->next;
printf("after removing a job...\n");
}
pthread_mutex_unlock(&job_queue_mutex);
if(next_job == NULL)
{
sleep(5);
continue;
}
process_job(next_job);
free(next_job);
}
return NULL;
}
void process_job(struct job* p)
{
printf("The value is : %d.\n", p->value);
}
void enqueue_job(struct job* new_job)
{
pthread_mutex_lock(&job_queue_mutex);
printf("begin inserting a job...\n");
new_job->next = job_queue;
job_queue = new_job;
printf("after inserting a job...\n");
pthread_mutex_unlock(&job_queue_mutex);
}
void * insert_queue_function(void * arg)
{
int i = 0;
while(i < 20)
{
sleep(1);
printf("put the value: %d.\n", i);
struct job* new_job = (struct job*)malloc(sizeof(struct job));
new_job->next = NULL;
new_job->value = i;
enqueue_job(new_job);
i++;
}
}
int main()
{
pthread_t insert_thread, process_thread;
pthread_create(&insert_thread, NULL, &insert_queue_function, NULL);
pthread_create(&process_thread, NULL, &process_queue_function, NULL);
pthread_join(insert_thread, NULL);
pthread_join(process_thread, NULL);
}
|
-
互斥锁有三种:
-
默认是快锁(fast mutex):两次锁定互斥锁会造成死锁。
-
递归锁(recursive mutex):允许多次锁定互斥锁,同样次数的解锁可以解除互斥锁。
-
检错锁(error checking mutex):第二次锁定互斥锁将返回错误码EDEADLK
-
相关推荐
- **第4章:线程** —— 探讨了多线程编程在Linux环境中的应用,涵盖了线程的创建、同步、互斥锁、条件变量等核心概念。 - **进程间通信(IPC):** - **第5章:进程间通信** —— 本章详细介绍了各种进程间通信...
《Linux程序设计第四版》是一本深受欢迎的编程教材,主要面向那些希望深入理解Linux操作系统下编程技术的读者。本书详细介绍了如何在Linux环境下编写高效、可靠的程序,涵盖了C语言编程、系统调用、进程管理、文件I/...
《Linux程序设计(第四版)》是一本深入探讨Linux系统编程的经典著作,涵盖了广泛的Linux程序设计主题。本书分为中文版和英文版,旨在满足不同语言背景的读者需求。作者Neil Mathew与Richard合作,结合了丰富的经验...
### 高级Linux程序设计知识点概述 #### 一、引言 《高级Linux程序设计》是一本专注于Linux平台上高级UNIX编程的技术书籍。该书由Mark Mitchell、Jeffrey Oldham和Alex Samuel共同撰写,并由完美废人(高远)翻译成...
《Linux程序设计第三版》是Linux系统编程领域的一本经典著作,它深入浅出地介绍了在Linux环境下进行程序开发所需的基础知识和高级技术。书中的源码覆盖了1到19章,这些章节包含了从基本的系统调用、文件操作到网络...
### Linux程序设计第四版知识点概览 #### 一、书籍基本信息 - **书名**:《Beginning Linux Programming》第四版 - **作者**:Neil Matthew 和 Richard Stones - **出版社**:Wiley Publishing, Inc. - **出版日期*...
《Linux高级程序设计》是学习Linux系统编程的重要参考资料,作者杨宗德的第三版源代码提供了丰富的实践案例,有助于读者深入理解Linux环境下的程序开发。本压缩包包含的文件是作者对书中理论知识的实现,涵盖了多种...
《Linux程序设计第四版》是Linux系统编程领域的一本经典著作,它深入浅出地讲解了如何在Linux环境下编写高效、可靠的程序。随书附带的源码是学习过程中的重要辅助材料,提供了丰富的实例,涵盖了从基本的系统调用到...
本资源“Linux程序设计第四版_源代码ch12”提供的正是该书第十二章的源代码,这对于学习者来说是极其宝贵的实践材料。通过分析和运行这些源代码,你可以更好地理解和掌握书中讲解的编程概念和技术。 在Linux环境中...
《Linux程序设计第四版》是Linux系统编程领域的一本经典著作,它深入讲解了如何在Linux环境下编写高效、可靠的程序。源代码ch17部分主要涉及第17章的内容,通常这一章会涵盖高级主题,如多线程、信号处理、网络编程...
《Linux程序设计第四版》是一本专为初学者设计的Linux编程指南,旨在帮助读者深入理解Linux操作系统下的程序开发过程。这本书涵盖了从基础概念到高级技术的广泛内容,旨在为读者提供全面的Linux编程知识。 在Linux...
目前Linux系统己很普及,但是介绍在Linux上进行开发的书籍并...Linux程序设计权威指南内容包括Linux开发环境、Linux编程的入门知识、系统和网络编程、多线程程序设计、控制台编程、XWindow系统编程、国际化编程知识等。
《Linux程序设计第四版》是深入理解Linux系统编程的经典之作,涵盖了从基本概念到高级技术的广泛内容。这本书旨在帮助开发者、系统管理员以及对Linux内核感兴趣的读者掌握编写高效、可靠的Linux程序所需的技能。 ...
《Linux高级程序设计》第三版是由杨宗德编著的一本深入探讨Linux系统下编程的经典教材。这本书旨在帮助读者理解并掌握在Linux环境下进行高效、...在实践中不断调试和修改这些代码,将有助于深化对Linux程序设计的理解。
《Linux程序设计第四版》是Linux系统编程领域的一本经典著作,主要涵盖了在Linux环境下进行程序设计的基础知识和高级技术。源代码ch10部分,很显然,对应的是书籍第十章的内容,通常会包含该章节涉及的所有示例程序...
通过对这些代码的阅读和修改,读者可以更深入地理解Linux程序设计的各种技术和陷阱。 总之,《Linux高级程序设计》第三版的源码集合是一份宝贵的教育资源,无论你是初学者还是经验丰富的开发者,都能从中受益匪浅。...