一、实验目的
- (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下创建一对父子进程。
- 提示 1:分别输出各自的进程号,父进程号和特别的提示字符串信息
- 提示 2:让父进程提前结束或后结束,观察子进程的父进程 ID。
- 提示 3:使用 PS 命令查看进程列表信息,核对进程号,父进程号
#include<unistd.h>
#include<stdio.h>
int main()
{
pid_t p1=fork();
if(p1>0){
printf(" 父进程进程号:%d\n",getpid());
printf(" 创建的子进程进程号:%d\n",p1);
printf(" 暂时挂起父进程以便ps检查结果(10s):\n");
sleep(2);
printf(" 父进程结束\n");
}else{
printf(" 子进程进程号:%d\n",getpid());
printf(" 父进程未结束时,子进程的父进程进程号:%d\n",getppid());
sleep(4);
printf(" \n父进程结束后,子进程的父进程进程号:%d\n",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 以标示是哪个线程输出的,并注意格式化输出信息。
1.编写代码
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
void* th_fn1(void *arg)
{
for(int i=1;i<=1000;i++){
printf("A:%d\n",i);
sleep(1);
}
}
void* th_fn2(void *arg)
{
for(int i=1000;i>0;i--){
printf("B:%d\n",i);
sleep(1);
}
}
int main()
{
int err;
pthread_t tid1,tid2;
if(err=pthread_create(&tid1,NULL,th_fn1,NULL)){
perror("pthread_create error");
}
if(err=pthread_create(&tid2,NULL,th_fn2,NULL)){
perror("pthread_create error");
}
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
return 0;
}
2.编译运行
注意要加入-lpthread ,才能使用线程。因为 pthread_create 等线程函数不在 C 标准库中。
gcc ./test2.c -o ./test2.out -lpthread
3.结果
实验三、在Windows下创建线程A和B,循环输出数据或字符串。
- 提示 1:使用 CreateThread 创建线程
- 提示 2:线程 A 递增输出 1-1000;线程 B 递减输出 1000-1。为避免输出太快,每隔 0.2 秒输出一个数。
- 提示 3:输出数据时,同时输出 A 或 B 标示是哪个线程输出的,并注意格式化输出信息
老师方法的代码
#include <iostream>
#include <thread>
#include <windows.h>
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<iostream>
#include <thread>
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;
}
运行结果
实验六、在Windows或Linux下利用线程实现“生产者-消费者”同步控制
- 提示 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++标准库 实现
对于个人来说,使用第二种方法 结果截图:
实验八、在Windows或Linux下模拟哲学家就餐,提供死锁和非死锁解法。
- 提示 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 <stdio.h>
#include <windows.h>
#include <time.h>
#include <string>
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开始思考,将思考%dms\n", id, time);
Sleep(time);
time = random();
printf("哲学家%d开始休息,将休息%dms\n", id, time);
Sleep(time);
WaitForSingleObject(left, INFINITE);
printf("哲学家%d取了左手边的筷子\t%d\n", id, id);
WaitForSingleObject(right, INFINITE);
printf("哲学家%d取了右手边的筷子\t%d\n", id, (id + 4) % 5);
time = random();
printf("哲学家%d开始吃饭,将吃饭%dms\n", id, time);
Sleep(time);
ReleaseSemaphore(left, 1, NULL);
printf("哲学家%d放下左手边的筷子\t%d\n", id, id);
ReleaseSemaphore(right, 1, NULL);
printf("哲学家%d放下右手边的筷子\t%d\n", 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) {
WaitForSingleObject(left, INFINITE);
printf("哲学家%d取了左手边的筷子\t%d\n", id, id);
WaitForSingleObject(right, INFINITE);
printf("哲学家%d取了右手边的筷子\t%d\n", id, (id + 4) % 5);
}
else {
WaitForSingleObject(right, INFINITE);
printf("哲学家%d取了右手边的筷子\t%d\n", id, (id + 4) % 5);
WaitForSingleObject(left, INFINITE);
printf("哲学家%d取了左手边的筷子\t%d\n", id, id);
}
完整代码
#undef UNICODE
#include <stdio.h>
#include <windows.h>
#include <time.h>
#include <string>
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开始思考,将思考%dms\n", id, time);
Sleep(time);
time = random();
printf("哲学家%d开始休息,将休息%dms\n", id, time);
Sleep(time);
if (id == 0) {
WaitForSingleObject(left, INFINITE);
printf("哲学家%d取了左手边的筷子\t%d\n", id, id);
WaitForSingleObject(right, INFINITE);
printf("哲学家%d取了右手边的筷子\t%d\n", id, (id + 4) % 5);
}
else {
WaitForSingleObject(right, INFINITE);
printf("哲学家%d取了右手边的筷子\t%d\n", id, (id + 4) % 5);
WaitForSingleObject(left, INFINITE);
printf("哲学家%d取了左手边的筷子\t%d\n", id, id);
}
time = random();
printf("哲学家%d开始吃饭,将吃饭%dms\n", id, time);
Sleep(time);
ReleaseSemaphore(left, 1, NULL);
printf("哲学家%d放下左手边的筷子\t%d\n", id, id);
ReleaseSemaphore(right, 1, NULL);
printf("哲学家%d放下右手边的筷子\t%d\n", 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 <stdio.h>
#include <windows.h>
#include <time.h>
#include <string>
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开始思考,将思考%dms\n", id, time);
Sleep(time);
time = random();
printf("哲学家%d开始休息,将休息%dms\n", id, time);
Sleep(time);
WaitForMultipleObjects(2, chops, true, INFINITE);
printf("哲学家%d同时取了两边的筷子\t%d,%d\n", id, id, (id + 4) % 5);
time = random();
printf("哲学家%d开始吃饭,将吃饭%dms\n", id, time);
Sleep(time);
ReleaseSemaphore(chops[0], 1, NULL);
printf("哲学家%d放下左手边的筷子\t%d\n", id, id);
ReleaseSemaphore(chops[1], 1, NULL);
printf("哲学家%d放下右手边的筷子\t%d\n", 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]);
}
}
|