- (1)理解进程/线程的概念和应用编程过程;
- (2)理解进程/线程的同步机制和应用编程;
- 1)在Linux下创建一对父子进程。
- 2)在Linux下创建2个线程A和B,循环输出数据或字符串。
- 3)在Windows下创建线程A和B,循环输出数据或字符串。
- 4)在Linux下创建一对父子进程,实验wait同步函数。
- 5)在Windows下利用线程实现并发画圆/画方。
- 6)在Windows或Linux下利用线程实现“生产者-消费者”同步控制
- 7)在Linux下利用信号机制实现进程通信。
- 8)在Windows或Linux下模拟哲学家就餐,提供死锁和非死锁解法。
- 2,6,8必做,其余任选2。课前提前预做,老师机房检查和答疑
- 一、实验目的
- 二、实验内容
- 三、实验要求
- 实验一,在Linux下创建一对父子进程。
- 实验二,在Linux下创建2个线程A和B,循环输出数据或字符串
- 1.编写代码
- 2.编译运行
- 3.结果
- 实验三、在Windows下创建线程A和B,循环输出数据或字符串。
- 实验六、在Windows或Linux下利用线程实现“生产者-消费者”同步控制
- 实验八、在Windows或Linux下模拟哲学家就餐,提供死锁和非死锁解法。
- 会导致死锁的算法
- 不会导致死锁的算法
- 完全不可能产生死锁的解法
- 提示 1:分别输出各自的进程号,父进程号和特别的提示字符串信息
- 提示 2:让父进程提前结束或后结束,观察子进程的父进程 ID。
- 提示 3:使用 PS 命令查看进程列表信息,核对进程号,父进程号
#include#include int main() { pid_t p1=fork(); if(p1>0){ printf(" 父进程进程号:%dn",getpid()); printf(" 创建的子进程进程号:%dn",p1); printf(" 暂时挂起父进程以便ps检查结果(10s):n"); sleep(2); printf(" 父进程结束n"); }else{ printf(" 子进程进程号:%dn",getpid()); printf(" 父进程未结束时,子进程的父进程进程号:%dn",getppid()); sleep(4); printf(" n父进程结束后,子进程的父进程进程号:%dn",getppid()); printf(" 子进程结束n"); } return 0; }
编译运行
gcc ./test.c -o test1.out ./test1.out
结果
使用 PS 命令查看进程列表信息,核对进程号,父进程号
ps -o pid,ppid,cmd
为什么没有./test1.out我也不知道!!!毁灭吧!
ps -ef|grep test1.out实验二,在Linux下创建2个线程A和B,循环输出数据或字符串
- 提示 1:使用 pthread 线程库
- 提示 2:线程 A 递增输出 1-1000;线程 B 递减输出 1000-1。为避免输出太快,每隔 0.2 秒(可自行调节)输出一个数。
- 提示 3:输出数据时,同时输出 A 或 B 以标示是哪个线程输出的,并注意格式化输出信息。
#include2.编译运行#include #include #include //定义线程1运行函数 void* th_fn1(void *arg) //传入void*数值 a { for(int i=1;i<=1000;i++){ printf("A:%dn",i); sleep(1); } } //定义线程2运行函数 void* th_fn2(void *arg) //传入void*数值 r { for(int i=1000;i>0;i--){ printf("B:%dn",i); sleep(1); } } int main() { int err;//定义错误存储 pthread_t tid1,tid2;//定义线程标识符 //创建tid1线程 if(err=pthread_create(&tid1,NULL,th_fn1,NULL)){ perror("pthread_create error"); } //创建tid2线程 if(err=pthread_create(&tid2,NULL,th_fn2,NULL)){ perror("pthread_create error"); } pthread_join(tid1, NULL); pthread_join(tid2, NULL); return 0; }
注意要加入-lpthread,才能使用线程。因为 pthread_create 等线程函数不在 C 标准库中。
gcc ./test2.c -o ./test2.out -lpthread3.结果 实验三、在Windows下创建线程A和B,循环输出数据或字符串。
- 提示 1:使用 CreateThread 创建线程
- 提示 2:线程 A 递增输出 1-1000;线程 B 递减输出 1000-1。为避免输出太快,每隔 0.2 秒输出一个数。
- 提示 3:输出数据时,同时输出 A 或 B 标示是哪个线程输出的,并注意格式化输出信息
老师方法的代码
#include#include #include DWORD WINAPI add(LPVOID) { for (int i = 1; i<=1000; i++) { std::cout << "A:" << i << std::endl; Sleep(5); } return 1; } DWORD WINAPI dec(PVOID pParam) { for (int i = 1000; i > 0; i--) { std::cout << "B:" << i << std::endl; Sleep(5); } return 0; } int main() { HANDLE hthread[2]; DWORD threadID; DWORD threadID1; hthread[0] = CreateThread(NULL, 0, add, (LPVOID)1, 0, &threadID); hthread[1] = CreateThread(NULL, 0, dec, (LPVOID)2, 0, &threadID1); WaitForMultipleObjects(2, hthread, TRUE, INFINITE); return 0; }
运行结果
从 C++11 开始,线程,成为了 C++ 标准库的一部分,所以我们可以不再使用 CreateThread 来创建线程,简简单单地使用 std::thread 即可。
而且,CreateThread 是平台相关的,而 std::thread 是跨平台的。
因此,干嘛用CreateThread 。
使用std::thread的代码
#include#include void func_1() { for (int i = 1; i <= 1000; i++) { std::cout << i << std::endl; } } void func_2() { for (int i = 1000; i > 0; i--) { std::cout << i << std::endl; } } int main() { std::thread t1(func_1); std::thread t2(func_2); t1.join(); t2.join(); return 0; }
运行结果
- 提示 1:使用数组(10 个元素)代替缓冲区。2 个输入线程产生产品(随机数)存到数组中;3 个输出线程从数组中取数输出。
- 提 示 2 : Windows 使 用 临 界 区 对 象 和 信 号 量 对 象 , 主 要 函 数EnterCriticalSection() 、 LeaveCriticalSection() 、 WaitForSingleObject() 、ReleaseSemaphore()。
- 提示 3:Linux 使用互斥锁对象和轻量级信号量对象,主要函数:sem_wait( ),sem_post( ),pthread_mutex_lock( ),pthread_mutex_unlock( )。
- 提示 4:生产者 1 的数据:1000-1999 (每个数据随机间隔 100ms-1s),生产者 2 的数据:2000-2999 (每个数据随机间隔 100ms-1s)。
- 提示 5:消费者每休眠 100ms-1s 的随机时间消费一个数据。
- 提示 6:屏幕打印(或日志文件记录)每个数据的生产和消费记录。
抄别人作业就好
Windows下模拟生产者消费者问题 使用Windows.h库下实现
《操作系统实验》C++实现生产者-消费者问题使用c++标准库实现
对于个人来说,使用第二种方法
结果截图:
- 提示 1:同时提供提供可能会带来死锁的解法和不可能死锁的解法。
- 提示 2:可能会带来死锁的解法参⻅课件。Windows 尝试使用临界区对象( EnterCriticalSection , LeaveCriticalSection ) ; Linux 尝 试 使 用 互 斥 锁(pthread_mutex_lock, pthread_mutex_unlock)
- 提示 3:完全不可能产生死锁的解法,例如:尝试拿取两只筷子,两只都能拿 则 拿 , 否 则 都 不 拿 。 Windows 尝 试 使 用 WaitForMultipleObjects, WaitForSingleObject 和互斥量对象 ReleaseMutex 等相关函数) Linux 尝试使用互斥锁 pthread_mutex_lock,pthread_mutex_trylock 等函数。
- 提示 4:[可选] 图形界面显示哲学家取筷,吃饭,放筷,思考等状态。
- 提示 5:为增强随机性,各状态间维持 100ms-500ms 内的随机时⻓。
借鉴博客:操原上机(三) 哲学家就餐问题的死锁与非死锁解法
会导致死锁的算法#undef UNICODE #include#include #include #include int i = 0; std::string name[5] = { "0","1","2","3","4" }; int a[5] = { 1,1,1,1,1 }; int random(void) { int a = time(NULL); srand(a); return (rand() % 400 + 100); } //子线程函数 DWORD WINAPI philosopher(LPVOID lpParam) { int id = i++; int time; HANDLE right, left; left = OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE, name[id].c_str());//通过信号量名,获得信号量对象句柄 right = OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE, name[(id + 4) % 5].c_str()); while (1) { time = random(); printf("哲学家%d开始思考,将思考%dmsn", id, time); Sleep(time); time = random(); printf("哲学家%d开始休息,将休息%dmsn", id, time); Sleep(time); //p(left) WaitForSingleObject(left, INFINITE); printf("哲学家%d取了左手边的筷子t%dn", id, id); //p(right) WaitForSingleObject(right, INFINITE); printf("哲学家%d取了右手边的筷子t%dn", id, (id + 4) % 5); //吃饭 time = random(); printf("哲学家%d开始吃饭,将吃饭%dmsn", id, time); Sleep(time); //v ReleaseSemaphore(left, 1, NULL); printf("哲学家%d放下左手边的筷子t%dn", id, id); ReleaseSemaphore(right, 1, NULL); printf("哲学家%d放下右手边的筷子t%dn", id, (id + 4) % 5); } } int main(void) { HANDLE S[5]; //五个信号量 HANDLE hThread[5]; //五个线程 for (int i = 0; i < 5; i++) { S[i] = CreateSemaphore(NULL, 1, 1, name[i].c_str()); } for (int i = 0; i < 5; i++) { hThread[i] = CreateThread(NULL, 0, philosopher, NULL, 0, NULL); } WaitForMultipleObjects(5, hThread, TRUE, INFINITE); //等待子线程运行 for (int i = 0; i < 5; i++) { CloseHandle(S[i]); } }
出现死锁
修改代码
if (id == 0) {//先取左边的筷子
//p(left)
WaitForSingleObject(left, INFINITE);
printf("哲学家%d取了左手边的筷子t%dn", id, id);
//p(right)
WaitForSingleObject(right, INFINITE);
printf("哲学家%d取了右手边的筷子t%dn", id, (id + 4) % 5);
}
else {//先取右边的筷子
//p(right)
WaitForSingleObject(right, INFINITE);
printf("哲学家%d取了右手边的筷子t%dn", id, (id + 4) % 5);
//p(left)
WaitForSingleObject(left, INFINITE);
printf("哲学家%d取了左手边的筷子t%dn", id, id);
}
完整代码
#undef UNICODE #include#include #include #include int i = 0; std::string name[5] = { "0","1","2","3","4" }; int a[5] = { 1,1,1,1,1 }; int random(void) { int a = time(NULL); srand(a); return (rand() % 400 + 100); } //子线程函数 DWORD WINAPI philosopher(LPVOID lpParam) { srand((unsigned)time(NULL)); int id = i++; int time; HANDLE right, left; left = OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE, name[id].c_str());//通过信号量名,获得信号量对象句柄 right = OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE, name[(id + 4) % 5].c_str()); while (1) { time = random(); printf("哲学家%d开始思考,将思考%dmsn", id, time); Sleep(time); time = random(); printf("哲学家%d开始休息,将休息%dmsn", id, time); Sleep(time); if (id == 0) {//先取左边的筷子 //p(left) WaitForSingleObject(left, INFINITE); printf("哲学家%d取了左手边的筷子t%dn", id, id); //p(right) WaitForSingleObject(right, INFINITE); printf("哲学家%d取了右手边的筷子t%dn", id, (id + 4) % 5); } else {//先取右边的筷子 //p(right) WaitForSingleObject(right, INFINITE); printf("哲学家%d取了右手边的筷子t%dn", id, (id + 4) % 5); //p(left) WaitForSingleObject(left, INFINITE); printf("哲学家%d取了左手边的筷子t%dn", id, id); } //吃饭 time = random(); printf("哲学家%d开始吃饭,将吃饭%dmsn", id, time); Sleep(time); //v ReleaseSemaphore(left, 1, NULL); printf("哲学家%d放下左手边的筷子t%dn", id, id); ReleaseSemaphore(right, 1, NULL); printf("哲学家%d放下右手边的筷子t%dn", id, (id + 4) % 5); } } int main(void) { HANDLE S[5]; //五个信号量 HANDLE hThread[5]; //五个线程 for (int i = 0; i < 5; i++) { S[i] = CreateSemaphore(NULL, 1, 1, name[i].c_str()); } for (int i = 0; i < 5; i++) { hThread[i] = CreateThread(NULL, 0, philosopher, NULL, 0, NULL); } WaitForMultipleObjects(5, hThread, TRUE, INFINITE); //等待子线程运行 for (int i = 0; i < 5; i++) { CloseHandle(S[i]); } }
运行结果未出现死锁
基本思想:只有当左右两边的筷子都可取时,同时取两边的筷子用餐。
#undef UNICODE #include#include #include #include int i = 0; std::string name[5] = { "0","1","2","3","4" }; int a[5] = { 1,1,1,1,1 }; int random(void) { int a = time(NULL); srand(a); return (rand() % 400 + 100); } //子线程函数 DWORD WINAPI philosopher(LPVOID lpParam) { srand((unsigned)time(NULL)); int id = i++; int time; HANDLE chops[2]; chops[0] = OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE, name[id].c_str()); chops[1] = OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE, name[(id + 4) % 5].c_str()); while (1) { time = random(); printf("哲学家%d开始思考,将思考%dmsn", id, time); Sleep(time); time = random(); printf("哲学家%d开始休息,将休息%dmsn", id, time); Sleep(time); //p WaitForMultipleObjects(2, chops, true, INFINITE);//true表面只有等待所有信号量有效时,再往下执行。(FALSE 当有其中一个信号量有效时就向下执行) printf("哲学家%d同时取了两边的筷子t%d,%dn", id, id, (id + 4) % 5); //吃饭 time = random(); printf("哲学家%d开始吃饭,将吃饭%dmsn", id, time); Sleep(time); //v ReleaseSemaphore(chops[0], 1, NULL); printf("哲学家%d放下左手边的筷子t%dn", id, id); ReleaseSemaphore(chops[1], 1, NULL); printf("哲学家%d放下右手边的筷子t%dn", id, (id + 4) % 5); } } int main(void) { HANDLE S[5]; //五个信号量 HANDLE hThread[5]; //五个线程 for (int i = 0; i < 5; i++) { S[i] = CreateSemaphore(NULL, 1, 1, name[i].c_str()); } for (int i = 0; i < 5; i++) { hThread[i] = CreateThread(NULL, 0, philosopher, NULL, 0, NULL); } WaitForMultipleObjects(5, hThread, TRUE, INFINITE); //等待子线程运行 for (int i = 0; i < 5; i++) { CloseHandle(S[i]); } }



