1.什么是线程?
让一个进程同时做两件事情或者多件事情,看起来是非常有用的,但用fork调用创建新进程的代价太高,在这种情况下,线程就能发挥作用,他能实现两件事情或者更多事情以一种非常紧密的方式同时发生。那么什么是线程呢?
在一个程序中的多个执行路线叫做线程,更准确的定义是:线程是一个进程内部的一个控制序列。弄清楚fork系统调用和创建新线程的区别非常重要。
当进程执行fork系统调用时,将创建出该进程的一份新副本。这个新进程拥有自己到底变量和自己的pid,它的时间调度也是独立的,它的执行几乎完全独立于父进程。
当在进程中创建一个新线程时,新的执行线程将拥有自己的栈(有自己的局部变量),但与他的创建者共享全局变量、文件描述符、信号处理函数和当前目录状态。
2.线程的优缺点
先来看一看线程的优缺点。
优点:
1.让程序看起来好像是在同时做两件事是很有用的。(例如:在编辑文档的同时对文档中的单词数进行实时统计。)
2.对于一个有多步操作的程序来说,使之分离为多个线程来执行,能改善程序的执行效率。(例如一个需要同时处理多个网络连接的服务器应用程序,线程可以让他在等待连接的时候同时做一些其他有用的事情)
3.一般而言,线程之间切换需要操作系统做的工作比进程之间的切换少得多,因此多线程的对资源的需求要远小于多个进程。
缺点:
1.编写多线程程序需要非常仔细的设计。
2.对多线程程序的调试要比对单线程的程序的调试困难的多。
3.多线程对于单核cpu来说,不一定更加有效率。
3.一个简单的多线程程序
#include<pthread.h>
int pthread_create(pthread_t *thread,pthread_attr_t *attr,void *(*start_routine)(void *),void *arg);
void pthread_exit(void *retval);
int pthread_create函数第一个参数是指向pthread_t类型数据的指针,线程被创建时,这个指针指向的变量中将被写入一个标识符,我们用该标识符来引用新线程。下一个参数用于设置线程属性,一般设置为空。后两个参数分别告诉我线程将要启动执行的函数和传递给该函数的参数。
线程通过调用pthread_exit函数终止执行,就如同进程在结束调用exit函数一样。这个函数的作用是,终止调用它的线程并返回一个指向某个对象的指针。
#include<pthread.h>
int pthread_join(pthread_t th,void **thread_return);
第一个参数指定了将要等待的线程,即pthread_create的返回值。第二个参数指向线程的返回值。该函数成功时返回0,失败时返回错误代码。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
void *thread_function(void *arg);
char message[] = "Hello World";
int main() {
int res;
pthread_t a_thread;
void *thread_result;
res = pthread_create(&a_thread, NULL, thread_function, (void *)message);
if (res != 0) {
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
printf("Waiting for thread to finish...\n");
res = pthread_join(a_thread, &thread_result);
if (res != 0) {
perror("Thread join failed");
exit(EXIT_FAILURE);
}
printf("Thread joined, it returned %s\n", (char *)thread_result);
printf("Message is now %s\n", message);
exit(EXIT_SUCCESS);
}
void *thread_function(void *arg) {
printf("thread_function is running. Argument was %s\n", (char *)arg);
sleep(3);
strcpy(message, "Bye!");
pthread_exit("Thank you for the CPU time");
}
为保证编译通过,请采用以下命令:
cc -D_REENTRANT thread1.c
-o
thread1
-lpthread
执行程序:
执行程序:
[aaa@localhost libFile]$ ./thread1
Waiting for thread to finish...
thread_function is running. Argument was Hello World
Thread joined, it returned Thank you for the CPU time
Message is now Bye!
Waiting for thread to finish...
thread_function is running. Argument was Hello World
Thread joined, it returned Thank you for the CPU time
Message is now Bye!
对于程序加以修改,来验证main(主线程)和新线程的执行时序:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
void *thread_function(void *arg);
int run_now = 1;
char message[] = "Hello World";
int main() {
int res;
pthread_t a_thread;
void *thread_result;
int print_count1 = 0;
res = pthread_create(&a_thread, NULL, thread_function, (void *)message);
if (res != 0) {
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
while(print_count1++ < 20) {
if (run_now == 1) {
printf("1");
run_now = 2;
}
else {
sleep(1);
}
}
printf("\nWaiting for thread to finish...\n");
res = pthread_join(a_thread, &thread_result);
if (res != 0) {
perror("Thread join failed");
exit(EXIT_FAILURE);
}
printf("Thread joined\n");
exit(EXIT_SUCCESS);
}
void *thread_function(void *arg) {
int print_count2 = 0;
while(print_count2++ < 20) {
if (run_now == 2) {
printf("2");
run_now = 1;
}
else {
sleep(1);
}
}
sleep(3);
}
执行程序:
[aaa@localhost libFile]$ ./thread2
12121212121212121212
Waiting for thread to finish...
Thread joined
12121212121212121212
Waiting for thread to finish...
Thread joined
在单核cpu上可能等待几秒钟后才能看到输出。从结果可以看出,2个线程是同时执行的,且共享run_now变量。
4.线程同步
4.1信号量同步
#include<semaphore.h>
int sem_init(sem_t *sem,int pshared,unsigned int value);
sem指向信号量对象,pshared参数控制信号量的类型,若值为0,则为当前进程的局部信号量,否则该信号量可以在多个进程之间共享。
#include<semaphore.h>
int sem_wait(sem_t * sem);
int sem_post(sem_t * sem);
int sem_destroy(sem_t * sem);
这2个函数都以一个指针为参数,该指针指向的对象是由sem_init条用初始化的信号量。
sem_post函数的作用是以原子操作的方式给信号量的值增加1。所谓原子操作:如果2个线程企图同时给有个信号量加1,它们之间不会互相干扰,而不像如果2个程序之间对同一个文件进行读取、增加、写入操作时可能会引起冲突。
sem_wait函数以原子操作的方式将信号量的值减1,但它会等待直到信号量有非零值才会开始减法操作。
sem_destroy函数清理该信号拥有的所以资源。如果企图清理的信号正在被一些线程等待,就会收到一个错误。
示例:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
void *thread_function(void *arg);
sem_t bin_sem;
#define WORK_SIZE 1024
char work_area[WORK_SIZE];
int main() {
int res;
pthread_t a_thread;
void *thread_result;
res = sem_init(&bin_sem, 0, 0);
if (res != 0) {
perror("Semaphore initialization failed");
exit(EXIT_FAILURE);
}
res = pthread_create(&a_thread, NULL, thread_function, NULL);
if (res != 0) {
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
printf("Input some text. Enter 'end' to finish\n");
while(strncmp("end", work_area, 3) != 0) {
fgets(work_area, WORK_SIZE, stdin);
sem_post(&bin_sem);
}
printf("\nWaiting for thread to finish...\n");
res = pthread_join(a_thread, &thread_result);
if (res != 0) {
perror("Thread join failed");
exit(EXIT_FAILURE);
}
printf("Thread joined\n");
sem_destroy(&bin_sem);
exit(EXIT_SUCCESS);
}
void *thread_function(void *arg) {
sem_wait(&bin_sem);
while(strncmp("end", work_area, 3) != 0) {
printf("You input %d characters\n", strlen(work_area) -1);
sem_wait(&bin_sem);
}
pthread_exit(NULL);
}
执行:
[aaa@localhost libFile]$ ./thread3
Input some text. Enter 'end' to finish
hello world!
You input 12 characters
welcome to china!
You input 17 characters
end
Waiting for thread to finish...
Thread joined
Input some text. Enter 'end' to finish
hello world!
You input 12 characters
welcome to china!
You input 17 characters
end
Waiting for thread to finish...
Thread joined
4.2互斥量同步(常用)
#include<pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *mutexattr);
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
这些函数成功是返回0,失败时返回错误代码。
pthread_mutex_init函数中的属性参数允许我们设置互斥量的属性,而属性控制着互斥量的行为。有一个问题是:如果程序试图对一个已经加了锁的互斥量调用pthread_mutex_lock,程序就会被阻塞,而又因为拥有互斥量的这个线程正是现在被阻塞的线程,所以互斥量就永远解锁不了,程序会被死锁。这个问题可通过设置互斥量属性来解决(暂不讨论)。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
void *thread_function(void *arg);
pthread_mutex_t work_mutex; /* protects both work_area and time_to_exit */
#define WORK_SIZE 1024
char work_area[WORK_SIZE];
int time_to_exit = 0;
int main() {
int res;
pthread_t a_thread;
void *thread_result;
res = pthread_mutex_init(&work_mutex, NULL);
if (res != 0) {
perror("Mutex initialization failed");
exit(EXIT_FAILURE);
}
res = pthread_create(&a_thread, NULL, thread_function, NULL);
if (res != 0) {
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
pthread_mutex_lock(&work_mutex);
printf("Input some text. Enter 'end' to finish\n");
while(!time_to_exit) {
fgets(work_area, WORK_SIZE, stdin);
pthread_mutex_unlock(&work_mutex);
while(1) {
pthread_mutex_lock(&work_mutex);
if (work_area[0] != '\0') {
pthread_mutex_unlock(&work_mutex);
sleep(1);
}
else {
break;
}
}
}
pthread_mutex_unlock(&work_mutex);
printf("\nWaiting for thread to finish...\n");
res = pthread_join(a_thread, &thread_result);
if (res != 0) {
perror("Thread join failed");
exit(EXIT_FAILURE);
}
printf("Thread joined\n");
pthread_mutex_destroy(&work_mutex);
exit(EXIT_SUCCESS);
}
void *thread_function(void *arg) {
sleep(1);
pthread_mutex_lock(&work_mutex);
while(strncmp("end", work_area, 3) != 0) {
printf("You input %d characters\n", strlen(work_area) -1);
work_area[0] = '\0';
pthread_mutex_unlock(&work_mutex);
sleep(1);
pthread_mutex_lock(&work_mutex);
while (work_area[0] == '\0' ) {
pthread_mutex_unlock(&work_mutex);
sleep(1);
pthread_mutex_lock(&work_mutex);
}
}
time_to_exit = 1;
work_area[0] = '\0';
pthread_mutex_unlock(&work_mutex);
pthread_exit(0);
}
[aaa@localhost libFile]$ ./thread4
Input some text. Enter 'end' to finish
hello world!
You input 12 characters
i love linux!
You input 13 characters
end
Waiting for thread to finish...
Thread joined