进程:程序代码+数据+变量(占用着系统内存)+文件描述符(打开的文件)+环境
p408模拟一个闹钟alarm.c
线程:新的执行线程拥有自己的栈存储局部变量,但与他的创建者共享全局变量、文件描述符、信号处理函数、当前目录状态。
#include
#include
#include
#include //pause函数
#include
static int alarm_fired = 0;//标志
void ding(int sig)//模拟闹钟,参数为信号
{
alarm_fired = 1;//闹钟唤醒,标志设为1
}
//告诉子进程在等待5秒后发送一个SIGALRM信号(超时警告)给他的父进程
int main()
{
pid_t pid;
printf("alarm application atarting\n");
pid = fork();//建立子进程
switch(pid){
case -1:
perror("fork failed");
exit(1);
case 0:
//child
sleep(5);//子进程等待五秒
kill(getppid(),SIGALRM);//向父进程发送一个超时警告信号
exit(0);
}
//父进程通过一个signal调用安排好捕获信号的工作,等待超时警告信号的到来
//并未在信号处理函数ding中使用printf,而是设置标志位,在main函数中printf完成消息输出
printf("waiting for alarm to go off\n");
(void) signal(SIGALRM, ding);
pause();//把程序暂时挂起,直到有一个信号出现为止,被信号中断时返回
if(alarm_fired)
printf("Ding!\n");
printf("Done\n");
exit(0);
}
//使用信号并挂起程序的执行很重要
//利用更健壮的信号接口sigaction代替signal
#include
#include
#include
void ouch(int sig)
{
printf("OUCH! - I got signal %d\n", sig);
}
int main()
{
struct sigaction act;//包含:信号处理函数、信号屏蔽字、标志
act.sa_handler = ouch;
sigemptyset(&act.sa_mask);//创建空的信号屏蔽字:当前被阻塞的一组信号,不能被当前信号接收到,阻止竞态的一种方式
act.sa_flags = 0;
sigaction(SIGINT, &act, 0);
while(1){
printf("Hello world!\n");
sleep(1);
}
}
//Ctrl + C组合键:看到消息
//终止:Ctrl+\组合键
信号量:可以控制对一组相同对象那个的访问,比如从5条可用的电话线中分配1条给某个线程的情况,更适合用计数信号量。
互斥量:控制任一时刻只能有一个线程可以访问一些共享内存
例子:用信号量和双线程实现统计从键盘输入的字符串个数
#include
#include
#include
#include
#include
#include //访问信号量函数
const int WORK_SIZE = 1024;
void *thread_function(void *arg);
sem_t bin_sem; //信号量
char work_area[WORK_SIZE];
int main()
{
int res;
pthread_t a_thread;
void *thread_result; //指向任意类型的指针
//信号量的创建并初始化,初始值设为0,这样新线程启动时,sem_wait函数调用会阻塞并等待信号量变为非0值
//int sem_init(sem_t *sem, int pshared, unsigned int value);
//pshared:控制信号量类型,0代表信号量是当前进程的局部信号量
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); //增加信号量的值,增加后,信号量不为0, 不在阻塞,执行线程函数,计数并减小一个信号量再次阻塞
}
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);
}
改进:让主线程等到统计线程完成字符串个数的统计后再继续执行,可以使用互斥量!
锁住某个对象,使得每次只能有一个线程访问它.
#include
#include
#include
#include
#include
#include
//使用互斥量对关键变量进行保护
#define WORK_SIZE 1024
char work_area[WORK_SIZE];
pthread_mutex_t work_mutex; //protects both work_area and time_to_exit
int time_to_exit = 0;
void *thread_function(void *arg);
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) //没有输入end,继续等待输入字符串
{
fgets(work_area, WORK_SIZE, stdin);
if (work_area[0] != '\0')
{
pthread_mutex_unlock(&work_mutex); //解锁以便副线程访问并统计
sleep(1);
}
else
{
break;
}
}
pthread_mutex_unlock(&work_mutex);
}
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", (int)strlen(work_area) - 1); //统计字符串,之后解锁
work_area[0] = '\0'; //第一个字符设置为NULL,用来通知字符个数已统计完成
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);
}
}
//end退出后,解锁等待下一次输入
time_to_exit = 1;
work_area[0] = '\0';
pthread_mutex_unlock(&work_mutex);
pthread_exit(0);
}
//note:这里没有判断是否加锁,解锁是否成功,而且通过轮询的方式获得结果的方法不是好的编程方式,实际中,应尽可能的使用信号量来避免这种情况