线程相关内容

线程

  • 一、介绍
  • 二、thread库
    • 1、构造函数
      • (1)函数
      • (2)说明
      • (3)注意
    • 2、join函数
    • 3、detach
    • 4、joinable函数
    • 5、get_id函数
  • 三、mutex的种类
    • 1、mutex
      • (1)介绍
      • (2)lock
      • (3)unlock
      • (4)try_lock
    • 2、recursive_mutex
      • (1)介绍
      • (2)示例代码
    • 3、timed_mutex
      • (1)介绍
      • (2)成员函数
  • 四、lock_guard
    • 1、介绍
    • 2、特性
    • 3、示例代码
  • 五、unique_lock
    • 1、介绍
    • 2、特性
  • 六、condition_variable
    • 1、介绍
    • 2、wait
      • (1)函数
      • (2)介绍
    • 3、等待一段时间
      • (1)函数
      • (2)介绍
    • 4、通知
  • 七、this_thread
    • 1、介绍
    • 2、常用的函数
    • 3、示例代码
  • 八、应用
    • 1、题目
    • 2、解析
      • (1)场景一
      • (2)场景二
      • (3)总结
    • 3、代码
    • 4、运行结果
  • 九、atomic
    • 1、介绍
    • 2、基本用法
    • 3、注意事项
    • 4、示例代码
  • 十、相关概念
    • 1、内存屏障
    • 2、并发
    • 3、并行
  • 十一、无锁编程
    • 1、介绍
    • 2、优点
    • 3、挑战
    • 4、示例代码
  • 十二、相关文章

一、介绍

  • 在C++11之前,涉及到多线程问题的都是和平台相关的,比如windows和Linux下各有自己的接口,这使得代码的可移植性比较差。
  • C++11中最重要的特性就是对线程进行支持了,使得C++在进行编程时不需要依赖第三方库。
  • C++11引入了thread库,为C++程序提供了基本的线程支持。这个库使得C++程序能够更方便地创建和管理线程,从而能够利用多核处理器的计算能力,提高程序的执行效率和响应速度。
  • 线程是操作系统中的一个概念,线程对象可以关联一个线程,用来控制线程以及获取线程的状态。
  • 当创建一个线程对象后,没有提供线程函数,则该对象实际没有对应任何线程。

二、thread库

1、构造函数

(1)函数

在这里插入图片描述

(2)说明

  • 默认构造函数构造一个不表示任何执行线程的线程对象。
  • 初始化构造函数构造一个线程对象,表示一个新的joinable执行线程。新的执行线程调用fn,将args作为参数传递(使用其左值或右值引用的衰减副本)。此构造的完成与调用此fn副本的开始同步。
  • 复制构造函数是被删除的,即无法复制线程对象。
  • 移动构造函数构造一个线程对象,获取由x表示的执行线程(如果有的话)。此操作不会以任何方式影响移动线程的执行,它只是传输其处理程序。执行该移动构造函数后,x对象不再表示任何执行线程。

(3)注意

  • 当创建一个线程对象后,并且给线程关联线程函数,该线程就被启动,与主线程一起运行。
  • 线程函数一般情况下可以是函数指针、仿函数和lambda表达式。
  • 线程函数的参数是以值拷贝的方式拷贝到线程栈空间中的。因此,即使线程参数为引用类型,在线程中修改后也不能修改外部实参,因为其实际引用的是线程栈中的拷贝,而不是外部实参。
  • 如果是类成员函数作为线程参数时,必须将this作为线程函数参数传递。

2、join函数

  • 该函数调用后会阻塞住主线程(执行该语句的线程),当join的线程结束(构造时调用的函数返回)后,主线程继续执行。此函数返回的时刻与线程中所有操作的完成同步。
  • 调用此函数后,线程对象变得不可连接(non-joinable),可以安全地销毁。
  • joinable的线程对象在销毁前应是joined或detached的。

3、detach

  • 将对象表示的线程与调用线程分离,即执行程序语句的主线程与调用函数的线程分离,允许它们彼此独立执行。
  • 两个线程都继续运行,不会以任何方式阻塞或同步。但当任何一个结束执行时,对应线程的资源都会被释放。
  • 调用此函数后,线程对象变成non-joinable的,可以安全地被销毁。

4、joinable函数

  • 检查调用函数的线程是否是joinable的。
  • 如果一个线程对象代表一个正在执行中的线程,那么它就是joinable的。
  • 如果它是默认构造的、已被move,即不表示任何执行线程、线程对象的状态已经转移给其他线程对象以及其任何一个成员被调用了join或detach成功的任何一种。线程对象都不是joinable的。

5、get_id函数

  • get_id函数的作用为获取线程id。
  • 如果线程对象是joinable的,则函数返回一个唯一标识线程的值。
  • 如果线程对象不是joinable的,则函数返回一个默认构造的成员类型为thread::id的对象。

三、mutex的种类

1、mutex

(1)介绍

  • 在C++中,mutex(互斥锁)是用于保护共享数据免受多线程并发访问造成的数据竞争和条件竞争的一种同步机制。
  • 通过使用mutex,我们可以确保在任何时刻只有一个线程可以访问被保护的代码段(通常称为临界区)。这有助于维护数据的完整性和一致性。

(2)lock

  • 锁定互斥锁,调用线程锁定互斥锁,必要时阻塞。
  • 如果该互斥锁当前没有被锁住,则调用线程将该互斥锁锁住,直到调用unlock之前,该线程一直拥有该锁。
  • 如果当前互斥锁被其他线程锁住,则当前的调用该函数(lock)的线程被阻塞住。
  • 如果当前互斥锁被当前调用该函数(lock)的线程锁住,而该线程还在申请该锁,则会产生死锁(deadlock)。死锁相关内容参见线程与多线程(二)。

(3)unlock

  • 解锁互斥锁,释放对它的所有权。

(4)try_lock

  • 如果当前互斥锁没有被其他线程占有,则该线程锁住该互斥锁,获取它的所有权,直到该线程调用unlock释放互斥锁。
  • 如果当前互斥锁被其他线程锁住,则当前调用该函数(try_lock)的线程返回 false,而不会被阻塞住。
  • 如果当前互斥锁被当前调用该函数(try_lock)的线程锁住,而该线程还在申请该锁,则会产生死锁(deadlock)。

2、recursive_mutex

(1)介绍

  • 在C++中,recursive_mutex是一个特殊的互斥锁(mutex),它允许同一个线程多次获得锁而不发生死锁。这是通过维护一个所有权计数来实现的,每次线程成功获得锁时,该计数就会增加;每次线程释放锁时,计数就会减少。只有当计数降到零时,锁才真正被释放,允许其他线程获得它,即互斥对象在这期间将保持锁定状态,直到其成员解锁被调用的次数与该所有权级别一样多。
  • 这种互斥锁在递归函数调用或者需要在同一个线程中多次锁定同一资源的场景中非常有用。与普通的mutex不同,mutex在同一个线程中多次尝试加锁会导致死锁,因为mutex不允许同一线程多次加锁。
  • 尽管recursive_mutex允许同一个线程多次加锁,但它并不应该作为解决设计问题的首选方法。在可能的情况下,应该尽量避免在同一个线程中多次请求相同的锁,因为这可能会隐藏代码中的潜在问题,并可能导致性能下降。

(2)示例代码

void TestFunc1()
{
	recursive_mutex rmx;
	rmx.lock();
	cout << "recursive_mutex lock one" << endl;
	rmx.lock();
	cout << "recursive_mutex lock two" << endl;
	rmx.lock();
	cout << "recursive_mutex lock three" << endl;

	rmx.unlock();
	rmx.unlock();
	rmx.unlock();

	cout << "recursive_mutex unlock over" << endl;
}

int main()
{
	thread t1(TestFunc1);
	t1.join();

	return 0;
}

3、timed_mutex

(1)介绍

  • 在C++中,timed_mutex是一个特殊的互斥锁(mutex),它提供了在尝试锁定互斥锁时设置超时时间的功能。这意味着如果互斥锁在指定的时间内没有被成功获取,那么尝试获取锁的操作将失败,而不是无限期地等待。
  • timed_mutex与recursive_mutex相似,但它关注的是锁定操作的超时机制,而不是是否允许同一线程多次锁定。
  • timed_mutex提供了两个成员函数try_lock_until和try_lock_for来支持超时锁定。
  • try_lock_until函数尝试锁定互斥锁,直到指定的时间点abs_time。如果互斥锁在abs_time之前变得可用并被成功锁定,则返回true;如果超时,即到达abs_time时仍未成功锁定,则返回false。
  • try_lock_for函数尝试锁定互斥锁,直到指定的时间段rel_time过期。如果互斥锁在rel_time指定的时间段内变得可用并被成功锁定,则返回true;如果超时,则返回false。
  • 使用timed_mutex时,通常会结合C++11引入的< chrono >库来指定超时时间。

(2)成员函数

在这里插入图片描述
在这里插入图片描述

四、lock_guard

1、介绍

  • lock_guard用于管理一个互斥量(mutex)的锁定和解锁,以简化同步代码的编写,并减少死锁的风险。
  • lock_guard通过作用域(RAII,Resource Acquisition Is Initialization)的方式来管理锁的生命周期,即在构造时自动加锁,在析构时自动解锁。这种方式确保了即使在发生异常的情况下,锁也能被正确释放。
  • RAII相关内容参见智能指针(RAII)。

2、特性

  • 不可复制:lock_guard是不可复制的,即不能将它的一个实例赋值给另一个实例,也不能将它作为函数参数传递,除非是通过引用或指针。这是因为复制或移动lock_guard可能会导致锁的所有权不明确,从而增加死锁的风险。
  • 不可递归:lock_guard不支持递归锁定,即不能在一个已经被lock_guard锁定的互斥量上再次使用lock_guard来加锁。

3、示例代码

void test_lock_guard()
{
	mutex mtx;
	int num = 0;
	thread t1([&]() {
		for (int i = 0; i < 500000; ++i)
		{
			lock_guard<mutex> lg(mtx);
			++num;
		}
		});
	thread t2([&]() {
		for (int i = 0; i < 500000; ++i)
		{
			mtx.lock();
			++num;
			mtx.unlock();
		}
		});

	t1.join();
	t2.join();
	cout << num << endl;
}

五、unique_lock

1、介绍

  • unique_lock提供了一种比lock_guard更灵活的互斥量(mutex)管理方式。与lock_guard类似,unique_lock也在构造时自动加锁,并在析构时自动解锁。但它还提供了更多的控制选项,比如手动解锁、条件变量支持、以及延迟锁定等。

2、特性

  • 可延迟锁定:unique_lock允许在构造时不立即加锁,而是稍后通过调用lock、try_lock或lock_interruptibly方法来加锁。
  • 可手动解锁:通过调用unlock方法,可以在任何时候手动解锁互斥量,并在之后再次加锁(如果需要的话)。
  • 条件变量支持:unique_lock与condition_variable一起使用时,可以更容易地实现线程间的同步和通信。
  • 可移动但不可复制:与lock_guard一样,unique_lock也是不可复制的,但它是可移动的。这意味着可以将unique_lock的一个实例赋值给另一个实例(通过移动语义),但不能通过复制来做到这一点。

六、condition_variable

1、介绍

  • condition_variable是C++11引入的一个同步原语,用于在多个线程之间同步执行。它通常与unique_lock一起使用,以允许一个或多个线程在某个条件成立之前等待。
  • condition_variable提供了两种主要的等待函数,即wait和wait_for/wait_until,以及一个通知函数notify_one和一个广播通知函数notify_all。

2、wait

(1)函数

在这里插入图片描述

(2)介绍

  • 原子地释放锁(lck),阻塞当前线程直到其他在此条件变量下的线程调用notify_one或notify_all,即在收到通知之前,当前线程(申请锁定lck的互斥体)的执行将被阻止。
  • 一旦收到由其他线程显式的通知(调用同一个condition_variable的notify相关函数),函数就会解除阻塞并调用lck.lock(),使lck处于与调用wait函数时(阻塞前)相同的状态。
  • 通常,函数会被另一个线程中对成员notify_one或成员notify_all的调用通知唤醒。但是,某些实现可能会在不调用任何这些函数的情况下产生虚假的唤醒调用。因此,使用该功能的用户应确保其恢复条件得到满足。
  • 如果指定了pred,则该函数仅在pred返回false时阻止,通知只能在其变为true时解除阻止线程,实现类似于while (!pred()) wait(lck); ,这对于检查虚假唤醒调用特别有用。
  • 参数pred是一个可调用的对象或函数,它不接受任何参数,并返回一个可以作为布尔值计算的值。它会被反复调用,直到它的计算结果为true。

3、等待一段时间

(1)函数

在这里插入图片描述
在这里插入图片描述

(2)介绍

  • wait_for的作用类似于wait,但线程只会在指定的相对时间rel_time内等待条件pred成立。如果时间到了但条件仍未成立,则函数返回,并且锁仍然被当前线程持有。
  • wait_until的作用类似于wait_for,但使用绝对时间点abs_time,而不是相对时间。

4、通知

  • notify_one:解除当前正在等待此条件变量condition的一个线程。如果没有线程在等待,则函数什么也不做;如果有多个线程,则选择哪个线程是未指定。
  • notify_all:解除当前正在等待此条件变量condition的所有线程。如果没有线程在等待,则函数什么也不做。

七、this_thread

1、介绍

  • this_thread是C++11及以后版本中引入的一个命名空间,它定义在头文件中。
  • this_thread命名空间提供了一系列函数,用于对当前线程执行各种操作,如睡眠、获取当前线程的ID等等。这对于编写多线程程序时控制或查询当前线程的行为非常有用。

2、常用的函数

  • sleep_for:使当前线程暂停执行指定的时间间隔。这个函数接受一个时间段作为参数,时间段类型可以是chrono库中的任何duration类型,如chrono::seconds、chrono::milliseconds等等。
  • sleep_until:使当前线程暂停执行,直到指定的时间点。这个函数接受一个时间点(chrono::time_point类型)作为参数,当前线程会在这个时间点之前保持休眠状态。
  • yield:提示操作系统重新调度当前线程,让出CPU给其他线程。但这并不意味着当前线程会被立即挂起或放弃其时间片,但它确实允许其他线程有机会运行。
  • get_id:获取当前线程的ID。每个线程都有一个唯一的ID,可以在多线程程序中用于标识对应的线程。

3、示例代码

void Test8()
{
	mutex mtx;
	int x = 0, n = 50;
	thread t1([&, n]() {
		for (int i = 0; i < n; ++i)
		{
			while (!mtx.try_lock())
				this_thread::yield();
			++x;
			cout << "t1 (" << this_thread::get_id() << "):" << i << endl;
			this_thread::sleep_for(chrono::milliseconds(50));
			mtx.unlock();
		}
		});

	thread t2([&, n]() {
		for (int i = 0; i < n; ++i)
		{
			while (!mtx.try_lock())	
				this_thread::yield();
			++x;
			cout << "t2 (" << this_thread::get_id() << "):" << i << endl;
			this_thread::sleep_for(chrono::milliseconds(50));
			mtx.unlock();
		}
		});

	t1.join();
	t2.join();

	cout << x << endl;
}

八、应用

1、题目

创建两个线程交替打印,一个线程只打印奇数,另一个只打印偶数。

2、解析

(1)场景一

  • 线程t1比t2先运行,t1先抢到互斥锁并lock,因为此时flag是false,则t1会先打印,再将flag改为true。接着执行后续代码,出了作用域,unique_lock自动解锁。
  • 如果此时线程t2还没启动,或者没有分到时间片。此时线程t1继续执行时就会执行条件变量的wait,t1上一次的notetify操作没有起到作用。但t2总会开始运行并打印,最后,它会将flag改成false,调用notify相关函数唤醒t1,后续线程t1与t2类似交替运行的方式进行打印。
  • 如果线程t2已经运行并且lock阻塞住了,则线程t1上一次的notify语句唤醒线程t2后解锁互斥锁。而线程t2获取到锁,因为此时falg是true,t2不会阻塞住,正常执行。后续线程t1与t2类似交替运行的方式进行打印。

(2)场景二

  • 线程t2比t1先运行,则t2获取到锁后会执行条件变量的wait,释放互斥锁并阻塞住。
  • 如果此时线程t1还没启动,或者没有分到时间片。但t1总会开始运行并打印,最后,它会将flag改成true,notify唤醒t2,后续线程t1与t2类似交替运行的方式进行打印。
  • 如果线程t1比t2慢启动,但是也分到时间片开始执行了,则t1在获取互斥锁时会lock阻塞住,当t2执行条件变量的wait时,会自动(unlock)释放锁,这时,t1会被唤醒并获取锁进行后续操作,后续线程t1与t2类似交替运行的方式进行打印。

(3)总结

  • 为了实现两个线程交替执行,需要保证其中一个线程先执行,上面的解析是保证线程t1先执行。
  • 通过一个bool类型的变量确定对应线程是否需要阻塞住让另一个线程先运行,由此达到两个线程交替运行的效果。

3、代码

void Test6()
{
	mutex mtx;
	condition_variable cv;
	int x = 1, n = 50;
	bool flag = false;

	thread t1([&, n] {
		for (int i = 0; i < n; ++i)
		{
			unique_lock<mutex> ul(mtx);
			if (flag)
				cv.wait(ul);
			cout << "t1(" << this_thread::get_id() << "): " << x << endl;
			++x;
			flag = true;
			cv.notify_one();
			this_thread::sleep_for(chrono::milliseconds(60));
		}
		});

	thread t2([&, n] {
		for (int i = 0; i < n; ++i)
		{
			unique_lock<mutex> ul(mtx);
			if (!flag)
				cv.wait(ul);
			cout << "t2(" << this_thread::get_id() << "): " << x << endl;
			++x;
			flag = false;
			cv.notify_one();
			this_thread::sleep_for(chrono::milliseconds(60));
		}
		});

	t1.join();
	t2.join();
}

4、运行结果

在这里插入图片描述

九、atomic

1、介绍

  • 在C++中,atomic类型和相关的操作提供了一种在多线程程序中安全地访问和操作单个数据项的方法。
  • 这是通过确保在读取和写入数据时操作的原子性来实现的,即这些操作在执行过程中不会被其他线程的操作打断。

2、基本用法

  • atomic可以用于任何整型、指针类型、以及用户定义的类型(只要它们满足特定的要求,如提供无锁的复制和赋值操作)。但是,对于用户定义的类型,更推荐使用atomic_flag或者通过atomic特化模板为bool类型(atomic< bool >),因为对于复杂的用户定义类型,确保原子性可能更为复杂且效率较低。

3、注意事项

  • 使用atomic可以帮助避免数据竞争,但它不解决所有并发编程中的问题,如死锁、活锁、饥饿等。
  • 对于复杂的同步需求,可能需要使用其他同步机制,如互斥锁(mutex)、条件变量(condition_variable)等。
  • 在某些情况下,过度使用atomic可能会降低性能,因为它可能会引入额外的开销,如内存屏障(memory barriers)。因此,在性能敏感的应用中,应该谨慎使用。

4、示例代码

void Test7()
{
	mutex mtx;
	int n = 100000;
	//size_t x = 0;
	atomic<size_t> x = 0;

	thread t1([&, n] {
		for (int i = 0; i < n; ++i)
			++x;
		});

	thread t2([&, n] {
		for (int i = 0; i < n; ++i)
			++x;
		});

	t1.join();
	t2.join();

	cout << "x = " << x << endl;
}

十、相关概念

1、内存屏障

  • 在C++(以及更广泛的C和C++11及以后版本)中,内存屏障(Memory Barrier)是一个重要的概念,特别是在编写多线程程序时。
  • 内存屏障用于控制内存访问的顺序,确保在屏障前后的操作按照预定的顺序执行,从而避免由于编译器优化或处理器乱序执行引起的内存访问问题。

2、并发

  • 并发是指两个或多个事件在同一时间间隔内发生,这些事件在宏观上看起来是同时进行的,但在微观上,即具体到某一时刻,只有一个事件在执行。
  • 并发通过快速切换多个任务来模拟同时执行的效果,实际上是任务在时间上的复用。
  • 并发侧重于在同一实体(如单个处理器或CPU核心)上同时处理多个任务。这种同时是通过时间片轮转、任务调度等方式实现的,每个任务轮流占用处理器资源。

3、并行

  • 并行是指两个或多个事件在同一时刻真正同时发生。
  • 并行要求有多个实体(如多个处理器或CPU核心)同时参与,每个实体独立执行一个任务。
  • 并行侧重于在不同实体上同时处理多个任务,每个任务由独立的处理器或CPU核心来执行,从而实现真正的并行处理。

十一、无锁编程

1、介绍

  • C++无锁编程(Lock-Free Programming)是一种并发编程技术,它旨在通过避免使用传统的互斥锁(mutexes)或其他同步机制来减少线程间的竞争和等待时间,从而提高程序的性能和可伸缩性。
  • 无锁编程通常依赖于原子操作(atomic operations)来确保数据的一致性和线程安全。

2、优点

  • 减少上下文切换:由于不需要等待锁,减少了线程因等待锁而阻塞的情况,从而减少了上下文切换的开销。
  • 减少死锁和活锁的风险:不使用锁可以避免死锁和活锁等并发问题。
  • 提高性能:在高并发场景下,无锁编程通常能提供更好的性能。

3、挑战

  • 复杂性:无锁编程通常需要更复杂的算法和逻辑来确保数据的一致性和线程安全。
  • 调试困难:无锁编程中的错误可能更难以发现和调试,因为问题可能表现为非重复性的、难以预测的行为。
  • 平台依赖性:不同的硬件和编译器对原子操作的支持程度不同,可能导致代码的可移植性降低。

4、示例代码

void Test9()
{
	mutex mtx;
	int n = 50;
	atomic<size_t> x = 0;
	thread t1([&, n]() {
		for (int i = 0; i < n; ++i)
		{
			size_t oldvl, newvl;
			do {
				oldvl = x;
				newvl = x + 1;
			} while (!atomic_compare_exchange_weak(&x, &oldvl, newvl));

			//cout << "t1 (" << this_thread::get_id() << "):" << i << endl;
			//this_thread::sleep_for(chrono::milliseconds(50));
		}
		});

	thread t2([&, n]() {
		for (int i = 0; i < n; ++i)
		{
			size_t oldvl, newvl;
			do {
				oldvl = x;
				newvl = x + 1;
			} while (!atomic_compare_exchange_weak(&x, &oldvl, newvl));

			//cout << "t2 (" << this_thread::get_id() << "):" << i << endl;
			//this_thread::sleep_for(chrono::milliseconds(50));
		}
		});

	t1.join();
	t2.join();

	cout << x << endl;
}

十二、相关文章

  • Linux下的线程相关内容参见线程与多线程(一)和线程与多线程(二)。

本文到这里就结束了,如有错误或者不清楚的地方欢迎评论或者私信
创作不易,如果觉得博主写得不错,请点赞、收藏加关注支持一下💕💕💕

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/873400.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

水晶连连看 - 无限版软件操作说明书

水晶连连看 – 无限版游戏软件使用说明书 文章目录 水晶连连看 – 无限版游戏软件使用说明书1 引言1.1 编写目的1.2 项目名称1.3 项目背景1.4 项目开发环境 2 概述2.1 目标2.2 功能2.3 性能 3 运行环境3.1 硬件3.2 软件 4 使用说明4.1 游戏开始界面4.2 游戏设定4.2.1 游戏帮助4…

长短期记忆神经网络-LSTM回归预测-MATLAB代码实现

一、LSTM简介&#xff08;代码获取&#xff1a;底部公众号&#xff09; 长短期记忆神经网络&#xff08;Long Short-Term Memory, LSTM&#xff09;是一种循环神经网络&#xff08;Recurrent Neural Network, RNN&#xff09;的变体。相比于传统的RNN&#xff0c;LSTM能够更好…

如何在mac上玩使命召唤手游?苹果电脑好玩的第一人称射击游戏推荐

《使命召唤4&#xff1a;现代战争》&#xff08;Call of Duty 4: Modern Warfare&#xff09;是由Infinity Ward开发并于2007年发行的第一人称射击游戏。该游戏是《使命召唤》系列的第四部作品&#xff0c;是一款非常受欢迎的游戏之一&#xff0c;《使命召唤4&#xff1a;现代战…

【Linux操作系统】线程控制

目录 一、线程创建二、线程等待三、线程退出四、线程的优缺点五、多线程的创建六、C11的多线程七、线程分离 一、线程创建 使用接口pthread_create创建新线程&#xff0c;头文件是pthread.h #include <iostream> #include <unistd.h> #include <pthread.h>…

2024国赛数学建模-模拟火算法(MATLAB 实现)

模拟退火算法 1.1 算法原理 模拟退火算法的基本思想是从一给定解开始 ,从邻域 中随机产生另一个解 ,接受 Metropolis准则允许目标函数在 有限范围内变坏 ,它由一控制参数 t决定 ,其作用类似于物 理过程中的温度 T,对于控制参数的每一取值 ,算法持续进 行“产生 —判断 —接受…

部署Apache网站

简易部署自己的apache网站 写在前面&#xff1a;先安装好mysql&#xff0c;再来搭建站点 1.安装php [rootlocalhost ~]# yum install php -y ##安装了php&#xff0c;默认会和apache结合工作2.创建文件编写php网页代码 [rootlocalhost ~]# vim /var/www/html/index.php ##创…

uniapp交互反馈

页面交互反馈可以通过:uni.showToast(object)实现,常用属性有 ioc值说明 值说明success显示成功图标&#xff0c;此时 title 文本在小程序平台最多显示 7 个汉字长度&#xff0c;App仅支持单行显示。error显示错误图标&#xff0c;此时 title 文本在小程序平台最多显示 7 个汉字…

【C++进阶】hash表的封装

文章目录 hash表哈希表的关键组成部分哈希表的优缺点优点&#xff1a;缺点&#xff1a; 常见应用场景 开放定址法实现hash表负载因子 (Load Factor)负载因子的意义负载因子的影响再散列 (Rehashing)示例 整体框架insertFinderasehash桶封装框架insertfinderase~HashTable() 总结…

Apache ShardingSphere数据分片弹性伸缩加解密中间件

Apache ShardingSphere Apache ShardingSphere 是一款分布式 SQL 事务和查询引擎,可通过数据分片、弹性伸缩、加密等能力对任意数据库进行增强。 软件背景 ShardingSphere是一套开源的分布式数据库中间件解决方案组成的生态圈,它由Sharding-JDBC、Sharding-Proxy和Sharding…

git 提交自动带上storyid

公司里的运维团队的产品经理&#xff0c;那老六提出说要在每个提交带上的jira storyid或者bugid&#xff0c;不用他自己弄不顾他人麻烦&#xff0c;真想问候他的xx。不过既然已经成为定局&#xff0c;还是想想有没有其他办法。经一番调研&#xff0c;网上有比较零碎的信息&…

Nginx 负载均衡+高可用 集群部署(Keepalived+LVS DR模式)

一、LVS负载均衡简介 1.1 LVS基本介绍 LVS&#xff08;Linux Virtual Server&#xff09;即Linux虚拟服务器&#xff0c;是由章文嵩博士主导开发的开源负载均衡项目&#xff0c;目前LVS已经被集成在Linux内核中。该项目在Linux内核中实现了基于IP地址的请求数据负载均衡调度方…

以太网交换机工作原理学习笔记

在网络中传输数据时需要遵循一些标准&#xff0c;以太网协议定义了数据帧在以太网上的传输标准&#xff0c;了解以太网协议是充分理解数据链路层通信的基础。以太网交换机是实现数据链路层通信的主要设备&#xff0c;了解以太网交换机的工作原理也是十分必要的。 1、以太网协议…

CTFHub技能树-Git泄漏-Stash

目录 一、前提知识 1.什么是git stash 2.git文件目录结构 3.git中对象指向 二、解题过程 方法一&#xff1a;使用GitHack 方法二&#xff1a;使用Git_Extract工具&#xff0c;这个是自动解析不用git stash等操作&#xff0c;直接得到flag 当前大量开发人员使用git进行版本…

SQL进阶技巧:截止当前批次前的批次量与订单量 | 移动窗口问题

目录 0 场景描述 1 数据准备 2 问题分析 3 小结 0 场景描述 表A有如下字段,user id(用户ID),batch id(批次ID),order id(订单ID),create time(创建时间),同一个用户ID下有多个批次,同一个批次下有多个订单ID,相同批次ID的创建时间是相同的,创建时间精确到了秒。 统计,截…

1-10 图像增强对比度 opencv树莓派4B 入门系列笔记

目录 一、提前准备 二、代码详解 enhanced_image cv2.convertScaleAbs(image, alpha1.5, beta0) 三、运行现象 四、完整工程贴出 一、提前准备 1、树莓派4B 及 64位系统 2、提前安装opencv库 以及 numpy库 3、保存一张图片 二、代码详解 import cv2 # 增强图像的对比度 …

【音视频】播放音视频时发生了什么? 视频的编解码 H264是什么? MP4是什么?

目录 ✨播放一个视频的流程✨为什么要编码&#xff08;压缩&#xff09;视频数据&#xff1f;✨如何编码&#xff08;压缩&#xff09;数据&#x1f384;简单的例子&#x1f384;音视频编码方式&#x1f384;视频编码格式H264编码是什么&#xff1f;发展历程&#xff1f;H.264基…

【Python游戏开发】拼图小游戏demo

使用mu编辑器 pgzero编写拼图小游戏 import randomSIZE 96 # 设置每张图块的大小 WIDTH SIZE * 3 # 根据土块大小设置窗口 HEIGHT SIZE * 3 pics [] # 存放图块 finished False # 游戏结束标识# 将前八张图块存放在pics列表中 for i in range…

009.Python爬虫系列_urllib模块案例

我 的 个 人 主 页:👉👉 失心疯的个人主页 👈👈 入 门 教 程 推 荐 :👉👉 Python零基础入门教程合集 👈👈 虚 拟 环 境 搭 建 :👉👉 Python项目虚拟环境(超详细讲解) 👈👈 PyQt5 系 列 教 程:👉👉 Python GUI(PyQt5)文章合集 👈👈 Oracle数…

传统CV算法——基于Opencv的多目标追踪算法

基于 OpenCV 的跟踪算法有多种&#xff0c;每种算法都有其特定的应用场景和优缺点。以下是一些常见的基于 OpenCV 的目标跟踪算法&#xff1a; 1. BOOSTING 跟踪器 描述&#xff1a;基于 AdaBoost 算法的跟踪器。它是一种早期的跟踪算法&#xff0c;使用的是基于弱分类器的强…

php转职golang第二期

以下是一份简单的 Go 基本语法笔记&#xff1a; 变量与常量&#xff1a; • var 声明变量。• const 声明常量。数据类型&#xff1a; • 整型、浮点型、布尔型、字符串型等。流程控制&#xff1a; • if-else 语句。• for 循环。函数&#xff1a; • 定义和调用函数。数…