进程与线程
线程拥有自己的栈(因为线程有自己的局部变量),但与它的创建者共享全局变量、文件描述符、信号句柄和当前目录状态。
通过fork创建子进程,创建出该进程的一份拷贝,这个新进程拥有自己的变量和自己的PID,它的时间调度是独立的,拥有独立的地址空间,它的执行几乎完全独立于父进程。
进程可以看成一个资源的基本单位,而线程是程序调度的基本单位,一个进程内部的线程之间共享进程获得的时间片。
_REENTRANT宏
编写的多线程程序,通过定义宏_REENTRANT来告诉编译器我们需要可重入功能,这个宏的定义必须出现于程序中的任何#include语句之前。
_REENTRANT为我们做三件事情,并且做的非常优雅:
(1)它会对部分函数重新定义它们的可安全重入的版本,这些函数名字一般不会发生改变,只是会在函数名后面添加_r字符串,如函数名gethostbyname变成gethostbyname_r。
(2)stdio.h中原来以宏的形式实现的一些函数将变成可安全重入函数。
(3)在error.h中定义的变量error现在将成为一个函数调用,它能够以一种安全的多线程方式来获取真正的errno的值。
线程的基本函数
1线程创建
#include <pthread.h>
int pthread_create(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);
参数说明:
thread:指向pthread_create类型的指针,用于引用新创建的线程。
attr:用于设置线程的属性,一般不需要特殊的属性,所以可以简单地设置为NULL。
*(*start_routine)(void *):传递新线程所要执行的函数地址。
arg:新线程所要执行的函数的参数。
调用如果成功,则返回值是0,如果失败则返回错误代码。
2线程终止
#include <pthread.h>
void pthread_exit(void *retval);
参数说明:
retval:返回指针,指向线程向要返回的某个对象。
线程通过调用pthread_exit函数终止执行,并返回一个指向某对象的指针。注意:绝不能用它返回一个指向局部变量的指针,因为线程调用该函数后,这个局部变量就不存在了,这将引起严重的程序漏洞。
3线程同步
#include <pthread.h>
int pthread_join(pthread_t th, void **thread_return);
参数说明:
th:将要等待的线程,线程通过pthread_create返回的标识符来指定。
thread_return:一个指针,指向另一个指针,而后者指向线程的返回值。
//thread1.c
/* 线程创建、同步和删除 */
#include "pthread.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void *thread_function(void *arg);
char message[] = "hello world!";
int main(void)
{
int res;
pthread_t a_thread;
void *thread_result;
/* 创建线程 */
res = pthread_create(&a_thread, NULL, thread_function, (void *)message);
if (0 != res)
{
perror("Thread create failed!");
exit(EXIT_FAILURE);
}
printf("Waiting for thread to finish...\r\n");
/* 线程同步,当前线程等待a_thread结束 */
res = pthread_join(a_thread, &thread_result);
printf("Thread joined, it returned %s\r\n", (char *)thread_result);
printf("Message is now %s\r\n", message);
exit(EXIT_FAILURE);
}
/* 新线程的回调函数 */
void *thread_function(void *arg)
{
printf("thread_function is running. Argument is %s\r\n", (char *)arg);
sleep(3);
/* 修改全局变量,这里也可以看出线程之间共享全局变量 */
strcpy(message, "Bye!");
/* 线程终止,返回某个对象的指针,不能是局部变量 */
pthread_exit("Thank you for your CPU time!");
}
4线程同时执行
利用一个原理:即除了局部变量外,所有其他的变量在一个进程中的所有线程之间是共享的。
在这个程序中,我们是在两个线程之间使用轮询技术,这种方式称为忙等待,所以它的效率会很低。
//thread2.c
/* 线程同步执行,通过对一个全局变量的轮询控制 */
#include "pthread.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void *thread_function(void *arg);
int flag = 1;
int main(void)
{
int res;
int count = 1;
pthread_t a_thread;
void *thread_result;
/* 创建线程 */
res = pthread_create(&a_thread, NULL, thread_function, NULL);
if (0 != res)
{
perror("Thread create failed!");
exit(EXIT_FAILURE);
}
while (count++ <= 20)
{
if (flag == 1)
{
printf ("1\r\n");
flag = 2;
}
else
{
sleep(1);
}
}
printf("Waiting for thread to finish...\r\n");
/* 线程同步,当前线程等待a_thread结束 */
res = pthread_join(a_thread, &thread_result);
printf("Thread joined, it returned %s\r\n", (char *)thread_result);
exit(EXIT_FAILURE);
}
/* 新线程的回调函数 */
void *thread_function(void *arg)
{
int count = 1;
while (count++ <= 20)
{
if (flag == 2)
{
printf("2\r\n");
flag = 1;
}
else
{
sleep(1);
}
}
/* 线程终止,返回某个对象的指针,不能是局部变量 */
pthread_exit("Thank you for your CPU time!");
}
5线程同步
5.1信号量同步
1.信号量创建
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
参数说明:
sem:信号量对象。
pshared:控制信号量的类型,0表示这个信号量是当前进程的局部信号量,否则,这个信号量就可以在多个进程之间共享。
value:信号量的初始值。
2.信号量控制
#include <semaphore.h>
int sem_wait(sem_t *sem);
int sem_post(sem_t *sem);
sem_post的作用是以"原子操作"的方式给信号量的值加1。
sem_wait的作用是以"原子操作"的方式给信号量的值减1,但它会等到信号量非0时才会开始减法操作。
如果对值为0的信号量调用sem_wait,这个函数就会等待,直到有线程增加了该信号量的值使其不再为0。
3.信号量销毁
#include <semaphore.h>
int sem_destory(sem_t *sem);
这个函数的作用是,用完信号量后对它进行清理,清理该信号量所拥有的资源。如果你试图清理的信号量正被一些线程等待,就会收到一个错误。
与大多数Linux函数一样,这些函数在成功时都返回0。
//thread3.c
/* 通过信号量sem_t实现同步 */
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <semaphore.h>
#define SIZE 1024
void *thread_function(void *arg);
char buffer[SIZE];
sem_t sem;
int main()
{
int res;
pthread_t a_thread;
void *thread_result;
/* 初始化信号量 */
res = sem_init(&sem, 0, 0);
if (res != 0)
{
perror("Sem init failed");
exit(EXIT_FAILURE);
}
res = pthread_create(&a_thread, NULL, thread_function, NULL);
if (res != 0)
{
perror("Thread create failed");
exit(EXIT_FAILURE);
}
printf("Input some text. Enter 'end' to finish\r\n");
while (scanf("%s", buffer))
{
/* 释放信号量 */
sem_post(&sem);
if (strncmp("end", buffer, 3) == 0)
break;
}
printf ("Waiting for thread to finish...\r\n");
res = pthread_join(a_thread, &thread_result);
if (res != 0)
{
perror("Thread join failed");
exit(EXIT_FAILURE);
}
printf ("Thread join\r\n");
/* 销毁信号量 */
sem_destroy(&sem);
exit(EXIT_SUCCESS);
}
void *thread_function(void *arg)
{
sem_wait(&sem);
while (strncmp("end", buffer, 3) != 0)
{
printf("You input %d characters\r\n", strlen(buffer));
/* 等待信号量,跳回主线程 */
sem_wait(&sem);
}
sleep(2);
printf ("thread finished\r\n");
pthread_exit(NULL);
}
5.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_destory(pthread_mutex_t *mutex);
与其他函数一样,成功时返回0,失败时将返回错误代码,但这些函数并不设置errno,所以必须对函数的返回代码进行检查。互斥量的属性设置这里不讨论,因此设置成NULL。
//thread4.c
/* 通过pthread_mutex_t实现互斥 */
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <semaphore.h>
#define SIZE 1024
char buffer[SIZE];
void *thread_function(void *arg);
pthread_mutex_t mutex;
int main()
{
int res;
pthread_t a_thread;
void *thread_result;
/* 初始化信号量 */
res = pthread_mutex_init(&mutex, NULL);
if (res != 0)
{
perror("Mutex init failed");
exit(EXIT_FAILURE);
}
res = pthread_create(&a_thread, NULL, thread_function, NULL);
if (res != 0)
{
perror("Thread create failed");
exit(EXIT_FAILURE);
}
printf("Input some text. Enter 'end' to finish\r\n");
while (1)
{
/* 操作前全局变量必须上锁 */
pthread_mutex_lock(&mutex);
scanf("%s", buffer);
/* 操作完解锁 */
pthread_mutex_unlock(&mutex);
if (strncmp("end", buffer, 3) == 0)
break;
sleep(1);
}
printf ("Waiting for thread to finish...\r\n");
res = pthread_join(a_thread, &thread_result);
if (res != 0)
{
perror("Thread join failed");
exit(EXIT_FAILURE);
}
printf ("Thread join\r\n");
/* 销毁信号量 */
pthread_mutex_destroy(&mutex);
exit(EXIT_SUCCESS);
}
void *thread_function(void *arg)
{
sleep(1);
while (1)
{
/* 操作前全局变量必须上锁 */
pthread_mutex_lock(&mutex);
printf("You input %d characters\r\n", strlen(buffer));
/* 操作完解锁 */
pthread_mutex_unlock(&mutex);
if (strncmp("end", buffer, 3) == 0)
break;
sleep(1);
}
sleep(2);
printf ("thread finished\r\n");
pthread_exit(NULL);
}
6多线程
pthread_create创建多个线程:
//thread5.c
/* 基本多线程创建 */
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#define NUM 8
void *thread_function(void *arg);
int main()
{
int res;
pthread_t a_thread[NUM];
void *thread_result;
int index;
for (index = 0; index < NUM; ++index) {
res = pthread_create(&a_thread[index], NULL, thread_function, (void *)index);
if (res != 0)
{
perror("Thread create failed!");
exit(EXIT_FAILURE);
}
sleep(1);
}
printf("Waiting for threads to finished...\r\n");
for (index = NUM - 1; index >= 0; --index)
{
res = pthread_join(a_thread[index], &thread_result);
if (res == 0)
{
printf("Picked up a thread:%d\r\n", index + 1);
}
else
{
perror("pthread_join failed\r\n");
}
}
printf("All thread done\r\n");
exit(EXIT_SUCCESS);
}
void *thread_function(void *arg)
{
int my_number = (int)arg;
int rand_num;
printf("thread_function is running. Argument was %d\r\n", my_number);
rand_num = 1 + (int)(9.0 * rand()/(RAND_MAX + 1.0));
sleep(rand_num);
printf("Bye from %d\r\n", my_number);
pthread_exit(NULL);
}
Makefile:
all:thread1 thread2 thread3 thread4 thread5 thread6 thread7 thread8
thread1: thread1.c
gcc -D_REENTRANT thread1.c -o thread1 -lpthread
thread2: thread2.c
gcc -D_REENTRANT thread2.c -o thread2 -lpthread
thread3: thread3.c
gcc -D_REENTRANT thread3.c -o thread3 -lpthread
thread4: thread4.c
gcc -D_REENTRANT thread4.c -o thread4 -lpthread
thread5: thread5.c
gcc -D_REENTRANT thread5.c -o thread5 -lpthread
thread6: thread6.c
gcc -D_REENTRANT thread6.c -o thread6 -lpthread
thread7: thread7.c
gcc -D_REENTRANT thread7.c -o thread7 -lpthread
thread8: thread8.c
gcc -D_REENTRANT thread8.c -o thread8 -lpthread
clean:
rm -f thread1 *.o thread2 thread3 thread4 thread5 thread6 thread7 thread8
【参考链接:http://blog.csdn.net/monkey_d_meng/article/details/5628663】