C++实现经典同步问题(生产者消费者、读者写者、哲学家进餐、吸烟者问题)

2023-05-16

一、生产者 - 消费者问题

  • 环境:windows

问题描述:
一组生产者进程和一组消费者进程共享一个初始为空、大小为〃的缓冲区,只有缓冲区没满时,生产者才能把消息放入缓冲区,否则必须等待;只有缓冲区不空时,消费者才能从中取出消息,否则必须等待。由于缓冲区是临界资源,它只允许一个生产者放入消息,或一个消费者从中取出消息。


代码:

#include <Windows.h>

#include <iostream>
#include <stdlib.h>

typedef HANDLE Semaphore;
#define P(S) WaitForSingleObject(S, INFINITE)
#define V(S) ReleaseSemaphore(S, 1, NULL)

int produceId = 1000;	//生产的的产品ID
int consumeId;			//消费的产品ID
int buf[10];			//用于存放缓存中的数据
int BUFSIZE = 10;	//缓存大小
int in = 0;				//用于表示生产者放入缓存的下表索引
int out = 0;			//用于表示消费者从缓存中那组的数据的下标索引

//初始化empty互斥量,初值为10,表示空的缓存数
Semaphore empty = CreateSemaphore(
	NULL,
	10,
	10,
	NULL
);

//初始化互斥量,用户互斥访问临界资源
Semaphore mutex = CreateSemaphore(
	NULL,
	1,
	1,
	NULL
);

//初始化full互斥量,初值为0,用于表示缓存中放了多少份数据
Semaphore full = CreateSemaphore(
	NULL,
	0,
	10,
	NULL
);

DWORD WINAPI Producer(LPVOID);	//生产者线程函数
DWORD WINAPI Consumer(LPVOID);	//消费者线程函数

VOID Produce();			//生产者生产数据 的函数
VOID Consume();			//消费者消费数据的函数 

VOID printbuf();		//用于打印缓存中的数据的函数


int main()
{
    
	HANDLE produce;
	HANDLE consumer1, consumer2, consumer3;

	produce = CreateThread(
		NULL,
		0,
		Producer,
		NULL,
		0,
		NULL
	);
	consumer1 = CreateThread(
		NULL,
		0,
		Consumer,
		NULL,
		0,
		NULL
	);
	consumer2 = CreateThread(
		NULL,
		0,
		Consumer,
		NULL,
		0,
		NULL
	);
	consumer3 = CreateThread(
		NULL,
		0,
		Consumer,
		NULL,
		0,
		NULL
	);
	while (1)
	{
		WaitForSingleObject(produce, INFINITE);
		WaitForSingleObject(consumer1, INFINITE);
		WaitForSingleObject(consumer2, INFINITE);
		WaitForSingleObject(consumer3, INFINITE);
	}

	return 0;
}

VOID printbuf() {
	std::cout << "缓存中的数据:";
	bool hasData = false;
	for (int i = 0; i < BUFSIZE; i++) {
		if (buf[i] != 0) {
			std::cout << buf[i] << "----";
			hasData = true;
		}
	}
	if (hasData)
		std::cout << std::endl;
	else
		std::cout << "无" << std::endl;
}

VOID Produce() {
	produceId++;
	buf[in++] = produceId;
	std::cout << "我是生产者,我生产了:" << produceId << std::endl;
	printbuf();
	in %= BUFSIZE;
}

VOID Consume() {
	consumeId = buf[out];
	std::cout << "我是消费者,我消费了:" << consumeId << std::endl;
	buf[out++] = 0;
	printbuf();
	out %= BUFSIZE;
}

DWORD WINAPI Producer(LPVOID) {
	while (1) {
		P(empty);		//有无空缓存
		P(mutex);		//互斥夹紧临界资源
		Produce();		//生产数据
		Sleep(1000);
		V(mutex);		//互斥夹紧临界资源
		V(full);		//full值+1
	}
	return 0;
}

DWORD WINAPI Consumer(LPVOID) {
	while (1) {
		P(full);		//缓存中有无数据
		P(mutex);		//互斥夹紧临界资源
		Consume();		//消费数据
		Sleep(1000);
		V(mutex);		//互斥夹紧临界资源
		V(empty);		//空缓存数加1
	}
	return 0;
}

运行效果:
在这里插入图片描述

再来看一个复杂一点的生产者 - 消费者问题:
桌子上有一个盘子,每次只能向其中放入一个水果。爸爸专向盘子中放苹果,妈妈专向盘子中放橘子,儿子专等吃盘子中的橘子,女儿专等吃盘子中的苹果。只有盘子为空时, 爸爸或妈妈才可向盘子中放一个水果;仅当盘子中有自己需要的水果时,儿子或女儿可以从盘子中取岀。


代码:

#include <Windows.h>

#include <iostream>
#include <stdlib.h>

typedef HANDLE Semaphore;
#define P(S) WaitForSingleObject(S, INFINITE)
#define V(S) ReleaseSemaphore(S, 1, NULL)

#define _NULL 0
#define _APPLE 1
#define _ORANGE 2
#define _FATHER 1
#define _MOTHER 2
#define _SON 3
#define _DAUGHTER 4

int what = 0;			//用于表示是哪种水果
int who = 0;			//用于表示是哪位放了或者吃了水果

//初始化apple互斥量,初值为0,用于表示盘中是否放了苹果
Semaphore apple = CreateSemaphore(
	NULL,
	0,
	1,
	NULL
);

//初始化orange互斥量,初值为0,用于表示盘中是否放了橘子
Semaphore orange = CreateSemaphore(
	NULL,
	0,
	1,
	NULL
);

//初始化plate互斥量,用于表示盘中是否有水果
Semaphore plate = CreateSemaphore(
	NULL,
	1,
	1,
	NULL
);

DWORD WINAPI Father(LPVOID);	//父亲线程函数
DWORD WINAPI Mother(LPVOID);	//母亲线程函数
DWORD WINAPI Son(LPVOID);		//儿子线程函数
DWORD WINAPI Daughter(LPVOID);	//女儿线程函数

VOID Put(int);			//父母放水果的函数
VOID Get();				//儿女吃水果的函数 

VOID PrintPlate();		//用于打印的函数


int main()
{

	HANDLE father, mother, son, daughter;;

	father = CreateThread(
		NULL,
		0,
		Father,
		NULL,
		0,
		NULL
	);
	mother = CreateThread(
		NULL,
		0,
		Mother,
		NULL,
		0,
		NULL
	);
	son = CreateThread(
		NULL,
		0,
		Son,
		NULL,
		0,
		NULL
	);
	daughter = CreateThread(
		NULL,
		0,
		Daughter,
		NULL,
		0,
		NULL
	);
	while (1)
	{
		WaitForSingleObject(father, INFINITE);
		WaitForSingleObject(mother, INFINITE);
		WaitForSingleObject(son, INFINITE);
		WaitForSingleObject(daughter, INFINITE);
	}

	return 0;
}

VOID PrintPlate() {
	std::string _who[4] = {
		"爸爸",
		"妈妈",
		"儿子",
		"女儿"
	};
	std::string _what[2] = {
		"苹果",
		"橘子"
	};
	if (who == _FATHER || who == _MOTHER) {
		std::cout << "我是:" << _who[who - 1].c_str()
			<< ";  我放了【" << _what[what - 1].c_str() << "】在盘子里...."
			<< std::endl;
	}
	else {
		std::cout << "我是:" << _who[who - 1].c_str()
			<< ";  我从盘子里把【" << _what[what - 1].c_str() << "】拿走了...."
			<< std::endl;
	}
}

VOID Put(int id) {
	what = (id == _APPLE) ? _APPLE : _ORANGE;
	who = (id == _FATHER) ? _FATHER : _MOTHER;
	PrintPlate();
}

VOID Get() {
	who = (what == _APPLE) ? _DAUGHTER : _SON;
	PrintPlate();
	what = _NULL;
}

DWORD WINAPI Father(LPVOID) {
	while (1) {
		P(plate);				//看盘子是否为空
		Put(_APPLE);			//父亲放入苹果
		Sleep(1000);
		V(apple);				//唤醒女儿线程
	}
	return 0;
}

DWORD WINAPI Mother(LPVOID) {
	while (1) {
		P(plate);				//看盘子是否为空
		Put(_ORANGE);			//母亲放入橘子
		Sleep(1000);
		V(orange);				//唤醒儿子线程
	}
	return 0;
}

DWORD WINAPI Son(LPVOID) {
	while (1) {
		P(orange);				//看盘子里是否有橘子
		Get();					//拿走橘子
		Sleep(1000);
		V(plate);				//盘子变空,唤醒父母
	}
	return 0;
}

DWORD WINAPI Daughter(LPVOID) {
	while (1) {
		P(apple);				//看盘子里是否有苹果
		Get();					//拿走苹果
		Sleep(1000);
		V(plate);				//盘子变空,唤醒父母
	}
	return 0;
}

运行效果:
在这里插入图片描述

二、读者 - 写者问题

问题描述:
有读者和写者两组并发进程,共享一个文件,当两个或以上的读进程同时访问共享数据时不会产生副作用,但若某个写进程和其他进程(读进程或写进程)同时访问共享数据时则可能导致数据不一致的错误。因此要求:①允许多个读者可以同时对文件执行读操作;②只允许一个写者往文件中写信息;③任一写者在完成写操作之前不允许其他读者或写者工作;④写者执行写操作前,应让已有的读者和写者全部退出。


读优先(读优先可能会导致写者线程饿死)的代码:

//读者优先,读者优先可能导致写进程饿死
#include <Windows.h>

#include <iostream>
#include <stdlib.h>

typedef HANDLE Semaphore;
#define P(S) WaitForSingleObject(S, INFINITE)
#define V(S) ReleaseSemaphore(S, 1, NULL)

int count = 0;			//用于记录读者数量

//初始化mutex互斥量,用于保证互斥访问count
Semaphore mutex = CreateSemaphore(
	NULL,
	1,
	1,
	NULL
);

//初始化rw互斥量,用于保证读写进程 互斥地访问文件
Semaphore rw = CreateSemaphore(
	NULL,
	1,
	1,
	NULL
);

DWORD WINAPI Reader(LPVOID);	//读者线程函数
DWORD WINAPI Writer(LPVOID);	//写者线程函数

VOID Read();				//读函数
VOID Write();				//写函数


int main()
{

	HANDLE reader[5];
	HANDLE writer[5];

	for (int i = 0; i < 5; i++) {
		reader[i] = CreateThread(
			NULL,
			0,
			Reader,
			NULL,
			0,
			NULL
		);
	}

	for (int i = 0; i < 5; i++) {
		writer[i] = CreateThread(
			NULL,
			0,
			Writer,
			NULL,
			0,
			NULL
		);
	}

	while (1)
	{
		for (int i = 0; i < 20; i++)
			WaitForSingleObject(reader[i], INFINITE);
		
		for (int i = 0; i < 5; i++)
			WaitForSingleObject(writer[i], INFINITE);
	}

	return 0;
}

VOID Read() {
	std::cout << "我是读线程,我正在读......." << std::endl;
}

VOID Write() {
	std::cout << "我是写线程,我正在写---------------" << std::endl;
}

DWORD WINAPI Reader(LPVOID) {
	while (1) {
		P(mutex);			//互斥访问count变量
		if (count == 0)		//当第一个读线程读共享文件时
			P(rw);			//阻塞写线程
		count++;			//读线程数加 1
		Sleep(1000);
		V(mutex);			//互斥访问count变量
		Read();				//读取共享文件
		P(mutex);			//互斥访问count变量
		count--;			//读线程数减 1
		if (count == 0)		//当当前没有读线程工作时
			V(rw);			//释放rw信号量,通知写线程可以工作了
		V(mutex);			//互斥访问count变量
	}
	return 0;
}

DWORD WINAPI Writer(LPVOID) {
	while (1) {
		P(rw);				//保证互斥访问共享文件
		Write();			//写共享文件
		Sleep(300);
		V(rw);				//保证互斥访问共享文件
	}
	return 0;
}

运行效果:
在这里插入图片描述
注:可以看到,由于读者线程优先,导致了写者线程饿死

写优先,或者叫读写平衡的代码:

//写者优先,或者叫读写平衡
#include <Windows.h>

#include <iostream>
#include <stdlib.h>

typedef HANDLE Semaphore;
#define P(S) WaitForSingleObject(S, INFINITE)
#define V(S) ReleaseSemaphore(S, 1, NULL)

int count = 0;			//用于记录读者数量

//初始化mutex互斥量,用于保证互斥访问count
Semaphore mutex = CreateSemaphore(
	NULL,
	1,
	1,
	NULL
);

//初始化rw互斥量,用于保证读写进程 互斥地访问文件
Semaphore rw = CreateSemaphore(
	NULL,
	1,
	1,
	NULL
);

//初始化w互斥量,用于实现“写优先”
Semaphore w = CreateSemaphore(
	NULL,
	1,
	1,
	NULL
);

DWORD WINAPI Reader(LPVOID);	//读者线程函数
DWORD WINAPI Writer(LPVOID);	//写者线程函数

VOID Read();				//读函数
VOID Write();				//写函数


int main()
{

	HANDLE reader[5];
	HANDLE writer[5];

	for (int i = 0; i < 5; i++) {
		reader[i] = CreateThread(
			NULL,
			0,
			Reader,
			NULL,
			0,
			NULL
		);
	}

	for (int i = 0; i < 5; i++) {
		writer[i] = CreateThread(
			NULL,
			0,
			Writer,
			NULL,
			0,
			NULL
		);
	}

	while (1)
	{
		for (int i = 0; i < 20; i++)
			WaitForSingleObject(reader[i], INFINITE);

		for (int i = 0; i < 5; i++)
			WaitForSingleObject(writer[i], INFINITE);
	}

	return 0;
}

VOID Read() {
	std::cout << "我是读线程,我正在读......." << std::endl;
}

VOID Write() {
	std::cout << "我是写线程,我正在写---------------" << std::endl;
}

DWORD WINAPI Reader(LPVOID) {
	while (1) {
		P(w);				//保证在无写线程时进入
		P(mutex);			//互斥访问count变量
		if (count == 0)		//当第一个读线程读共享文件时
			P(rw);			//阻塞写线程
		count++;			//读线程数加 1
		Sleep(1000);
		V(mutex);			//互斥访问count变量
		V(w);				//释放w,保证写线程优先
		Read();				//读取共享文件
		P(mutex);			//互斥访问count变量
		count--;			//读线程数减 1
		if (count == 0)		//当当前没有读线程工作时
			V(rw);			//释放rw信号量,通知写线程可以工作了
		V(mutex);			//互斥访问count变量
	}
	return 0;
}

DWORD WINAPI Writer(LPVOID) {
	while (1) {
		P(w);				//在无写线程时进入
		P(rw);				//保证互斥访问共享文件
		Write();			//写共享文件
		Sleep(300);
		V(rw);				//保证互斥访问共享文件
		V(w);				//写完毕,释放
	}
	return 0;
}

运行效果:
在这里插入图片描述
注:可以看到写者线程被调度了

三、哲学家进餐问题

问题描述:
一张圆桌边上坐着5名哲学家,每两名哲学家之间的桌上摆一根筷子,两根筷子中间是一碗米饭,如图2.12所示。哲学家们倾注毕生精力用于思考和进餐,哲学家在思考时,并不影响他人。只有当哲学家饥饿时,才试图拿起左、右两根筷子(一根一根地拿起)。若筷子已在他人手上,则需要等待。饥饿的哲学家只有同时拿到了两根筷子才可以开始进餐,进餐完毕后,放下筷子继续思考。

代码:

#include <Windows.h>

#include <iostream>
#include <stdlib.h>

typedef HANDLE Semaphore;
#define P(S) WaitForSingleObject(S, INFINITE)
#define V(S) ReleaseSemaphore(S, 1, NULL)

//筷子互斥量,初值为1,表示是否有筷子
Semaphore chopstick[5];

//初始化mutex互斥量,用于保证互斥访问chopstick
Semaphore mutex = CreateSemaphore(
	NULL,
	1,
	1,
	NULL
);

DWORD WINAPI Philosopher(LPVOID);	//哲学家线程函数

VOID InitSemaphore();

VOID Eat(int);							//哲学家进餐函数
VOID Think(int);						//哲学家思考函数

int main()
{
	InitSemaphore();

	HANDLE philosopher[5];
	int id[5];
	for (int i = 0; i < 5; i++)
		id[i] = i;

	for (int i = 0; i < 5; i++) {
		philosopher[i] = CreateThread(
			NULL,
			0,
			Philosopher,
			(LPVOID)&(id[i]),
			0,
			NULL
		);
	}

	while (1)
	{
		for (int i = 0; i < 5; i++)
			WaitForSingleObject(philosopher[i], INFINITE);
	}

	return 0;
}

VOID InitSemaphore() {
	for (int i = 0; i < 5; i++) {
		chopstick[i] = CreateSemaphore(
			NULL,
			1,
			1,
			NULL
		);
	}
}

VOID Eat(int i) {
	std::cout << "哲学家【" << i + 1 << "】正在进餐..." << std::endl;
}

VOID Think(int i) {
	std::cout << "哲学家【" << i + 1 << "】正在思考........" << std::endl;
}

DWORD WINAPI Philosopher(LPVOID i) {
	while (1) {
		P(mutex);								//互斥访问筷子
		P(chopstick[*((int *)i)]);				//拿起左手边的筷子
		P(chopstick[((*((int *)i)) + 1) % 5]);	//拿起右手边的筷子
		V(mutex);								//互斥访问筷子
		Eat(*(int *)i);							//进餐
		Sleep(1000);
		V(chopstick[*((int *)i)]);				//放下左手边的筷子
		V(chopstick[((*((int *)i) + 1)) % 5]);	//放下右手边的筷子
		Think(*(int *)i);						//思考.....在这里可能发生竟态条件
	}
	return 0;
}

运行效果:
在这里插入图片描述
注:以上思考的输出出现混乱的原因:

在一个哲学家线程调用到 Think 函数的时候由于没有信号量的保护,因此可能发生竟态条件,由于线程异步地执行,可能在某个线程运行 Think 的时候发生了调度。

四、吸烟者问题

问题描述:
假设一个系统有三个抽烟者进程和一个供应者进程。每个抽烟者不停地卷烟并抽掉它,但要卷起并抽掉一支烟,抽烟者需要有三种材料:烟草、纸和胶水。三个抽烟者中,第一个拥有烟草,第二个拥有纸,第三个拥有胶水。供应者进程无限地提供三种材料,供应者每次将两种材料放到桌子上,拥有剩下那种材料的抽烟者卷一根烟并抽掉它,并给供应者一个信号告诉已完成,此时供应者就会将另外两种材料放到桌上,如此重复(让三个抽烟者轮流地抽烟)。

代码:

#include <Windows.h>

#include <iostream>
#include <stdlib.h>

typedef HANDLE Semaphore;
#define P(S) WaitForSingleObject(S, INFINITE)
#define V(S) ReleaseSemaphore(S, 1, NULL)

#define YanCaoAndZhi 1
#define YanCaoAndJiaoShui 2
#define ZhiAndJiaoShui 3

int num;			//供应者供应用的 随机数
int what;			//表示桌上有什么材料

//初始化offerYanCao互斥量,用于表示供应烟草和纸
Semaphore offerYanCaoAndZhi = CreateSemaphore(
	NULL,
	0,
	1,
	NULL
);

//初始化offerZhi互斥量,用于表示供应烟草和胶水
Semaphore offerYanCaoAndJiaoShui = CreateSemaphore(
	NULL,
	0,
	1,
	NULL
);

//初始化offerJiaoShui互斥量,用于表示供应纸和胶水
Semaphore offerZhiAndJiaoShui = CreateSemaphore(
	NULL,
	0,
	1,
	NULL
);

//初始化finish互斥量,用于表示吸烟完成
Semaphore finish = CreateSemaphore(
	NULL,
	0,
	1,
	NULL
);

DWORD WINAPI Producer(LPVOID);	//供应者线程函数
DWORD WINAPI Smoker1(LPVOID);	//1号吸烟者,拥有烟草
DWORD WINAPI Smoker2(LPVOID);	//2号吸烟者,拥有纸
DWORD WINAPI Smoker3(LPVOID);	//3号吸烟者,拥有胶水

VOID Put(int);					//供应者放材料
VOID Smoke();				//吸烟者吸烟


int main()
{

	HANDLE producer;
	HANDLE smoker1, smoker2, smoker3;

	
	producer = CreateThread(
		NULL,
		0,
		Producer,
		NULL,
		0,
		NULL
	);

	smoker1 = CreateThread(
		NULL,
		0,
		Smoker1,
		NULL,
		0,
		NULL
	);

	smoker2 = CreateThread(
		NULL,
		0,
		Smoker2,
		NULL,
		0,
		NULL
	);

	smoker3 = CreateThread(
		NULL,
		0,
		Smoker3,
		NULL,
		0,
		NULL
	);

	while (1)
	{
		WaitForSingleObject(producer, INFINITE);
		WaitForSingleObject(smoker1, INFINITE);
		WaitForSingleObject(smoker2, INFINITE);
		WaitForSingleObject(smoker3, INFINITE);
	}

	return 0;
}

VOID Put(int i) {
	if (i == YanCaoAndZhi) {
		what = YanCaoAndZhi;
		std::cout << "我是供应者,我现在要把【烟草】和【纸】放到桌上" << std::endl;
	}
	else if (i == YanCaoAndJiaoShui) {
		what = YanCaoAndJiaoShui;
		std::cout << "我是供应者,我现在要把【烟草】和【胶水】放到桌上" << std::endl;
	}
	else {
		what = ZhiAndJiaoShui;
		std::cout << "我是供应者,我现在要把【纸】和【胶水】放到桌上" << std::endl;
	}
}

VOID Smoke() {
	if (what == YanCaoAndZhi) {
		std::cout << "我是吸烟者【3】号,我拥有【胶水】,我现在从桌上拿走【烟草】和【纸】,我开始吸烟了"
			<< std::endl;
	}	
	else if (what == YanCaoAndJiaoShui) {
		std::cout << "我是吸烟者【2】号,我拥有【纸】,我现在从桌上拿走【烟草】和【胶水】,我开始吸烟了"
			<< std::endl;
	}
	else {
		std::cout << "我是吸烟者【1】号,我拥有【烟草】,我现在从桌上拿走【纸】和【胶水】,我开始吸烟了"
			<< std::endl;
	}
	what = 0;
}

DWORD WINAPI Producer(LPVOID) {
	while (1) {
		num++;							//要放入什么
		num = num % 3;					
		if (num == 0) {
			Put(YanCaoAndZhi);			//放入烟草和纸
			V(offerYanCaoAndZhi);		//唤醒拥有胶水的吸烟者
		}
		else if (num == 1) {
			Put(YanCaoAndJiaoShui);		//放入烟草和胶水
			V(offerYanCaoAndJiaoShui);	//唤醒拥有纸的吸烟者
			
		}
		else {
			Put(ZhiAndJiaoShui);		//放入纸和胶水
			V(offerZhiAndJiaoShui);		//唤醒拥有烟草的吸烟者
		}
		Sleep(1000);
		P(finish);						//是否有吸烟者吸完烟了?
	}
	return 0;
}

DWORD WINAPI Smoker1(LPVOID){			//拥有烟草的吸烟者
	while (1) {
		P(offerZhiAndJiaoShui);			//供应者是否放好了纸和胶水
		Smoke();						//拿走材料开始吸烟
		Sleep(1000);
		V(finish);
	}
	return 0;
}

DWORD WINAPI Smoker2(LPVOID) {			//拥有纸的吸烟者
	while (1) {
		P(offerYanCaoAndJiaoShui);		//供应者是否放好了烟草和胶水
		Smoke();						//拿走材料开始吸烟
		Sleep(1000);
		V(finish);
	}
	return 0;
}

DWORD WINAPI Smoker3(LPVOID) {			//拥有胶水的吸烟者
	while (1) {
		P(offerYanCaoAndZhi);			//供应者是否放好了烟草和纸
		Smoke();						//拿走 材料开始吸烟
		Sleep(1000);
		V(finish);
	}
	return 0;
}

运行效果:
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++实现经典同步问题(生产者消费者、读者写者、哲学家进餐、吸烟者问题) 的相关文章

随机推荐

  • 新版本VSCode的eslint使用指导

    1 VSCode版本 xff1a 2 VSCode插件栏搜索 eslint 安装 xff1b 3 开启eslint xff1a 在首选项的用户设置里搜索formatOnSave 勾选 xff1b 4 安装eslint npm install
  • crontab、flock、timeout的配合使用实现定时任务超时及报警

    需求场景 开发后台的同学 xff0c 经常需要写一些脚本定时启动运行 xff0c 通常使用crontab来实现 有时候还有更加苛刻的需求 xff1a 脚本不仅需要定时启动 xff0c 而且还需要保证互斥 xff08 同一时间只有一个进程在跑
  • (原创)Flutter基础入门:手把手教你搭建Flutter混合项目:AAR方式集成

    前言 上一篇博客讲了如何用 模块代码依赖模式 这种方式去搭建Flutter混合项目 因为篇幅原因 xff0c AAR集成方式来搭建项目的步骤和注意点放到这篇博客来讲 如果你没看过上篇博客 xff0c 建议先阅读上一篇 xff1a xff08
  • Linux基础知识之systemd详解

    0x00 Systemd 简述 1 Linux 启动流程 2 主角登场 0x01 Systemd Unit 1 配置文件 2 启动流程 3 进程树 Process tree 4 运行级别 0x02 Systemd Manager 1 Sys
  • IntelliJ IDEA中有什么让你相见恨晚的好用插件?

    hello xff0c 大家好 xff0c 我是 Jackpop xff0c 硕士毕业于哈尔滨工业大学 xff0c 曾在华为 阿里等大厂工作 xff0c 如果你对升学 就业 技术提升等有疑惑 xff0c 不妨交个朋友 xff1a 我是Jac
  • Android串口的使用(转载+移植)

    提示 xff1a 文章写完后 xff0c 目录可以自动生成 xff0c 如何生成可参考右边的帮助文档 文章目录 前言一 移植java文件二 移植jni文件总结 前言 最近需要在上层app使用串口 发送指令对底层硬件进行操作 xff0c 因此
  • webstorm 代码格式检测

  • Android NFC基础入门

    说道NFC 是Near Field Communication缩写 xff0c 就是近距离无线通讯技术 NFC采用主动和被动两种通信模式 工作的模式有三种 xff0c 读写标签 xff0c 点对点 xff0c 仿真卡模式 xff0c 今天我
  • 嵌入式系统实践 12 ——基于ARM汇编 Keil5 MSP432 P401R开发板

    物联网实验1 阿里云远程控制小灯 span class token comment span span class token comment span span class token comment MSP432P401 span sp
  • Linux下使用rpm包安装MySQL数据库

    root 64 Hadoop102 df h df 34 34 没有那个文件或目录 df 34 h 34 没有那个文件或目录 root 64 Hadoop102 df h 文件系统 容量 已用 可用 已用 挂载点 dev mapper ce
  • Linux下SSH登录的三种方式

    SSH登录 SSH密钥登录 xff1a SSH默认采用密码登录 xff0c 这种方法有很多缺点 xff0c 简单的密码不安全 xff0c 复杂的密码不容易记忆 xff0c 每次手动输入也很麻烦 密钥登录是更好的解决方案 接下来介绍三种SSH
  • 在失望中重找希望——我的2013年工作总结

    时间过的真的是快 来广州已整整工作了一年啦 从2012年长沙工作离职后 为了我的女朋友 我毅然踏上了南下广州的征途 来到羊城后 很快 xff0c 一个礼拜就找到了现在工作的这家公司 现在回想一下 真觉得当初没有好好斟酌一下 2013年里 x
  • ubuntu root开机自登录 与 ros 节点自启

    1 systemctl命令及开机自启 新建a sh文件 放入 cd etc vim a sh xff01 usr bin env bash bash c 34 source opt ros melodic setup bash amp am
  • (原创)详解Glide图片加载库常用方法

    前言 Glide作为安卓开发常用的图片加载库 xff0c 有许多实用而且强大的功能 xff0c 那么 xff0c 今天就来总结一番 xff0c 这次把比较常见的都写出来 xff0c 但并不是全部哦 在介绍之前 xff0c 先来说说什么是Gl
  • 如何将本地windows文件复制到远程windows服务器

    如何使用windows自带的远程桌面连接程序将文件复制到远程服务器内 运行中输入mstsc xff0c 点击确定 输入服务器的IP和端口 xff0c 点击右下角的选项 然后点击本地资源选项卡 点击详细信息按钮 此时勾选你所要连接到服务器上的
  • 强国的语言与语言强国

    强国的语言与语言强国 作者 xff1a 教育部语言文字信息管理司司长 教育部语言文字应用研究所所长 中国社会科学院研究生 李宇明 理论上语言是一律平等的 xff0c 现实中语言是有强有弱的 语言的强弱与语言所属社团的强弱盛衰呈正相关 古罗马
  • 前端post请求报400错误

    前端post请求报400错误 发布时间 xff1a 2019 06 05 18 28 54 400的错误是由于发送请求参数没有成功 原因 xff1a 参数名称类型没有对上 xff1b 或者参数格式不正确 参数格式不正确 xff0c 解决方法
  • Redis集群及其中间遇到的坑

    Redis集群 集群准备 下载并解压Resis xff1a wget http download redis io releases redis 4 0 0 tar gz进入Redis目录下make amp amp make install
  • springmvc的相关配置文件

    最近自己搭了一个小的spring 43 springmvc 43 mybatis的秒杀相关的小工程 xff0c 下面跟大家分享一下 xff0c 用到的配置文件是怎么配置的 xff0c 供参考 我把相关的说明都放在注释中 xff0c 方便阅读
  • C++实现经典同步问题(生产者消费者、读者写者、哲学家进餐、吸烟者问题)

    一 生产者 消费者问题 环境 xff1a windows 问题描述 xff1a 一组生产者进程和一组消费者进程共享一个初始为空 大小为 的缓冲区 xff0c 只有缓冲区没满时 xff0c 生产者才能把消息放入缓冲区 xff0c 否则必须等待