【C++11】线程库

文章目录

  • thread 线程库
  • mutex 锁
  • atomic 原子性操作
  • condition_variable 条件变量
  • 实现两个线程交替打印1-100


thread 线程库

在C++11之前,涉及到多线程问题,都是和平台相关的,比如Windows和Linux下各有自己的接口,这使得代码的可移植性比较差。C++11中最重要的特性就是对线程进行支持了,使得C++在并行编程时不需要依赖第三方库,而且在原子操作中还引入了原子类的概念。要使用标准库中的线程,必须包含< thread >头文件。

在这里插入图片描述

thread类中的接口如下:

构造函数:

在这里插入图片描述

  • 支持无参构造,即构造一个空线程对象,由于该线程对象不会和任何外部线程关联,也没有关联的线程函数,因此不能直接开始执行线程,无参构造通常需要配合移动赋值来使用。
  • 支持构造一个线程对象,并关联线程函数,构造函数中的可变参数是传递给线程函数的参数,这种线程对象一旦创建就会开始执行。同时支持移动构造,即使用一个将亡对象来构造一个新的线程对象。

赋值重载:

在这里插入图片描述

  • 线程不允许两个非将亡对象之间的赋值,只允许将一个将亡对象赋值给另一个非将亡对象,即移动赋值,移动赋值的最常见用法是构造一个匿名线程对象,然后将其赋值给一个空线程对象。

其他相关接口:

在这里插入图片描述

  • get_id: 获取当前线程的id,即线程的唯一标识——bool joinable() const noexcept。
  • joinable: 用于检查当前线程对象是否与某个底层线程相关联,从而判断是否需要对线程对象进行join() 或者 detach 操作——bool joinable() const noexcept。
  • join: 由于线程是进程中的一个执行单元,同时线程的所有资源也是有进程分配的,所以主线程在结束之前需要对其他从线程进行join。即判断从线程是否全部执行完毕,如果执行完毕,就回收从线程资源并继续向后执行。如果存在未执行完毕的从线程,主线程就会阻塞在join语句处等待从线程,直到所有从线程都执行完毕——void join()。
  • detach: 将当前线程与主线程分离,分离后主线程不能再join当前线程。当前线程的资源会被自动回收——void detach()。

thread使用注意事项:

  1. 线程是操作系统中的一个概念,线程对象可以关联一个线程,用来控制线程以及获取线程的状态。当创建一个线程对象后,没有提供线程函数,该对象实际没有对应任何线程。
    在这里插入图片描述
  2. 当创建一个线程对象后,并且给线程关联线程函数,该线程就被启动,与主线程一起运行。线程函数一般情况下可按照 函数指针、lambda表达式和函数对象 三种方式提供。在这里说明一下:lambda表达式的本质其实是匿名的函数对象。
    在这里插入图片描述
    由于创建线程时,这些线程的执行顺序完全是由操作系统来进行调度的,因此thread 1/2/3 的输出顺序也是不确定的。
  3. 我们可以通过joinable() 函数来判断线程是否有效,如果是以下几种情况。则线程无效:采用无参构造函数构造的线程对象、线程对象的状态已经转移给其他线程对象、线程已经调用join或者detach结束。
  4. 线程函数的参数是以值拷贝的方式拷贝到线程栈空间中的,因此即使线程参数为引用类型,在线程中修改后也不能修改外部实参。因为实际引用的是线程栈中的拷贝,而不是外部的实参。
    在这里插入图片描述
    在这里插入图片描述
  5. 进程具有独立性,所以一个进程的退出并不会影响其他进程的正常运行。但是线程并不是独立的,一个进程下的多个线程共享进程的地址空间,所以一个线程的奔溃会导致整个进程奔溃。

this_thread 命名空间

C++11thread 头文件中,除了有thread类,还存在一个this_thread命名空间,它保存了线程的一些相关属性。

yield 函数

在这里插入图片描述

sleep_for 函数

在这里插入图片描述

面试题:并行和并发的区别?

并发和并行都是指多个任务同时执行的情况,但是它们的含义有所不同。

  • 并发是指在同一个时间段内,多个任务交替地执行,这些任务可以在同一台计算机上运行,也可以在不同的计算机上运行,彼此之间通过网络或其他方式进行通信和同步。并发常常用来提高系统的吞吐量和响应性,以及实现资源共享和负载均衡等功能。

  • 而并行是指在同一个时间点上,多个任务同时执行,这些任务通常在多个处理器或多个计算机上运行,每个任务分配给不同的处理器或计算机进行处理。并行常常用来加速计算和处理速度,提高系统的性能。


mutex 锁

C++11 中引入了一个新的 mutex 类,mutex是一个可锁定对象,它用于在多个线程之间锁定共享资源以防止竞争条件。

当我们对程序当中的某一部分代码加锁之后,线程如果想要执行这部分代码就必须先申请锁。当访问完毕后再释放锁,同时,一把锁在同一时间只能被一个线程持有,当其他线程再来申请锁时,会直接申请失败。从而阻塞或不断重新申请,直到持有锁的线程释放。通过以上策略,就能保证多个线程只能串行的访问临界区中的代码/数据,从而保证了线程安全问题。

在这里插入图片描述

mutex 的主要接口如下:

  • 构造: 互斥锁仅支持无参构造,不支持拷贝构造
    在这里插入图片描述
  • lock: 加锁函数。如果当前锁没有被任何线程持有,则当前线程持有锁并加锁,如果当前锁已经被其他线程持有,则当前线程阻塞直到持有锁的线程释放锁,如果当前互斥量被当前调用线程锁住,则会产生死锁。
  • try_lock: 尝试加锁函数。如果当前锁没有被任何线程持有,则当前线程持有锁并加锁。如果当前锁已经被其他线程持有,则加锁失败返回false,但当前线程并不会阻塞,而是跳过临界区代码继续向后执行。如果当前互斥量被当前调用线程锁住,则会产生死锁。
  • unlock: 解锁函数。当前线程执行完毕临界区中的代码后释放锁。如果存在其他线程正在申请当前锁,则它们其中一个将会持有锁并继续向后执行。当前锁也可能重新被当前线程竞争得到。
int x = 0;
mutex mtx;
void Func(int n)
{
	for (int i = 0; i < n; i++) {
		mtx.lock();
		++x;
		mtx.unlock();
	}
}

在这里插入图片描述

在这个示例中,我们创建了两个线程 t1 和 t2,它们都要修改全局变量x。这里我们使用了一把全局互斥锁来保护共享变量x,保护x不会被多个线程同时访问。

关于 lock_guard

lock_guard 是一种用于管理互斥锁的 RAII(Resource Acquisition Is Initialization)类。它可以保证在作用域结束时自动释放互斥锁,以避免忘记手动释放锁所导致的问题。

使用 lock_guard 类可以避免手动管理互斥锁的问题,可以提高程序的可读性和可维护性。在创建 lock_guard 对象时,需要传入一个互斥锁对象,这个互斥锁对象会被 lock_guard 类包装,当 lock_guard 对象被销毁时,它会自动调用互斥锁对象的 unlock 函数,释放互斥锁。

lock_guard 类的使用非常简单,只需要在需要使用互斥锁的代码块中创建一个 lock_guard 对象即可,不需要手动加锁和解锁。当程序流程离开这个代码块时,lock_guard 对象会自动释放互斥锁。由于 lock_guard 对象的生命周期是由编译器控制的,因此无论代码流程中出现何种异常情况,lock_guard 对象都会在作用域结束时被自动销毁,从而保证了程序的正确性。

lock_guard的模拟实现

template <class Lock>
class LockGuard
{
public:
	LockGuard(Lock& lock)
		: _lock(lock)
	{
		_lock.lock();
	}

	~LockGuard()
	{
		_lock.unlock();
	}
	
	LockGuard(const LockGuard&) = delete;
	LockGuard& operator=(const LockGuard&) = delete;

private:
	Lock& _lock;
};

LockGuard的使用

int x = 0;
mutex mtx;
void Func(int n)
{
	for (int i = 0; i < n; i++)
	{
		try
		{
			LockGuard<mutex> lock(mtx);
			++x;

			// 抛异常
			if (rand() % 3 == 0)
			{
				throw exception("抛异常");
			}
		}
		catch (const std::exception& e)
		{
			cout << e.what() << endl;
		}
	}
}

在这里插入图片描述

在 C++11 线程库中,线程执行例程的参数可以是引用也可以是拷贝,具体取决于用户如何传递参数。如果将参数作为值传递,则会进行拷贝构造,而如果将参数作为引用传递,则不会进行拷贝构造。

如果需要真正实现传递引用,可以使用 std::ref 函数将引用类型的参数包装成 std::reference_wrapper 类型的对象,然后将这个对象作为参数传递给线程的执行例程。 std::reference_wrapper 是一个模板类,它提供了一种引用的包装方式,可以像普通对象一样进行拷贝和赋值,同时可以通过调用其 get 函数获取其包装的引用。

如果有一个函数需要传递一个引用类型的参数,可以使用 ref 函数将这个引用包装成 reference_wrapper 类型的对象,并将其作为参数传递给线程的执行例程。

注:线程构造函数的参数包并不是直接传给执行例程的,而是先用参数包的参数去构造线程,然后再将这些参数传递给线程的执行例程。互斥锁没有被识别成引用传递的问题是出现在构造线程时参数包传递的过程。线程函数的参数是以值拷贝的方式拷贝到线程栈空间中的,因此:即使线程参数为引用类型,在线程中修改后也不能修改外部实参,因为其实际引用的是线程栈中的拷贝,而不是外部实参。

unique_lock

与 lock_guard 类似,unique_lock 类模板也是采用 RAII 的方式对锁进行了封装,并且也是以独占所有权的方式来管理 mutex 对象的上锁和解锁操作,即其对象之间不能发生拷贝。

与 lock_guard 不同的是,unique_lock 更加的灵活,提供了更多的成员函数:

  • 上锁/解锁操作:lock、try_lock、try_lock_for、try_lock_until 和 unlock。
  • 修改操作:移动赋值、交换 (swap:与另一个unique_lock对象互换所管理的互斥量所有权)、释放 (release:返回它所管理的互斥量对象的指针,并释放所有权)。
  • 获取属性:owns_lock (返回当前对象是否上了锁)、operator bool() (与 owns_lock() 的功能相同)、mutex (返回当前 unique_lock 所管理的互斥量的指针)。

unique_lock 和 lock_guard 最大的区别在于 lock_guard 无法手动释放和重新获取互斥锁,只能在创建时 lock,析构时 unlock,这在某些复杂的多线程编程场景中可能会受到一些限制。而 unique_lock 则提供了更加灵活和精细的互斥锁控制,unique_lock 可以在任何时刻手动地释放和重新获取互斥锁,并且支持不同的互斥锁处理策略,例如延时加锁、尝试加锁等。


std::recursive_mutex

recursive_mutex允许同一个线程对互斥量多次上锁(即递归上锁),来获得对互斥量对象的多层所有权,释放互斥量时需要调用与该锁层次深度相同次数的 unlock,除此之外,recursive_mutex 的特性和 mutex 大致相同。

std::timed_mutex

比 std::mutex 多了两个成员函数,try_lock_fortry_lock_until

  • try_lock_for:接受一个时间范围,表示在这一段时间范围之内线程如果没有获得锁则被阻塞住(与 mutex 的 try_lock 不同,try_lock 如果被调用时没有获得锁则直接返回 false),如果在此期间其他线程释放了锁,则该线程可以获得对互斥量的锁,如果超时(即在指定时间内还是没有获得锁),则返回 false。
  • try_lock_until:接受一个时间点作为参数,在指定时间点未到来之前线程如果没有获得锁则被阻塞住,如果在此期间其他线程释放了锁,则该线程可以获得对互斥量的锁,如果超时(即在指定时间内还是没有获得锁),则返回 false。

这里我们需要注意的是,在实际开发中try_lock_for() 和 try_lock_until()并不常用,其中对于时间的控制也比较复杂,因此这里我们只需要了解即可。

在这里插入图片描述

这里我们可以看到,当前的这种情况下,在整个for循环的外面加锁的效率会更高,这是因为整个CPU的速度很快,如果我们对++x进行加锁,那么CPU就会频繁的在t1和t2两个线程之间切换,并且t1和t2也需要频繁的加锁和解锁,而这些操作都是要消耗资源的。

在这里插入图片描述


atomic 原子性操作

虽然我们可以通过加锁来对共享资源进行保护,但加锁存在一定的缺陷,比如多个线程只能串行访问被锁包含的资源,会导致程序的运行效率降低。同时,加锁不当还会导致死锁的问题。因此C++11引入了原子性操作。原子操作不可被中断的一个或一系列操作,C++11通过引入原子操作类型,使得线程间数据的同步变得更加高效。

在这里插入图片描述

由于原子类型通常属于资源型数据,多个线程只能访问单个原子类型的拷贝,因此在C++11中,原子类型只能从其模板参数中进行构造,不允许原子类型进行拷贝构造、移动构造以及赋值重载等。

在这里插入图片描述

atomic类 主要支持原子性的 ++、--、+、-、按位与、按位或以及按位异或操作

atomic类能够支持这些原子性操作本质是因为其底层对CAS操作进行了封装,可以简单的理解为 atomic = CAS + while


CAS 操作

CAS (compare and swap) 是 CPU 硬件同步原语,它是支持并发的第一个处理器提供原子的测试并设置操作。CAS 操作包含三个操作数 – 内存位置(V)、预期原值(A)和新值 (B)。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则处理器不做任何操作。

我们还是以 ++g_val 操作为例,和一般的 ++ 操作不同,CAS 在会额外使用一个寄存器来保存讲寄存器中 g_val 修改之前的值 (预期原值),并且在将修改之后的值 (新值) 写回到内存时会重新取出内存中 g_val 的值与预期原值进行比较,如果二者相等,则将新值写入内存;如果二者不等,则放弃写入。

这样当线程 A 将新值写入到内存之前,如果有其他线程对 g_val 的值进行了修改,则内存中 g_val 的值就会与预期原值不等,此时操作系统就会放弃写入来保证整个 ++ 操作的原子性。

但单纯的放弃写入会导致可能当前 ++ 操作执行了但是 g_val 的值并不变;所以 C++ 对 CAS 操作进行了封装,即在 CAS 外面套了一层 while 循环,当新值成功写入时跳出循环,当新值写入失败时重新执行之前的取数据、修改数据、写回数据的操作,直到新值写入成功。这样做的优点是即实现了语言层面上 ++ 操作的原子性,解决了其线程安全问题;缺点是有一些 ++ 操作可能要重复执行多次才能成功,一定程度上影响程序效率,但还是比加锁解锁的效率要高。

注: 上面只是对 atomic 底层原理的简单理解,atomic 底层逻辑控制肯定不是单纯的 CAS + while 这么简单的,但作为一般程序员这样理解也就够了。感兴趣的可以看一下陈皓大佬的这一篇文章 无锁队列的实现

int main()
{
	int n = 100000;

	atomic<int> 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();
	size_t end = clock();

	printf("%d\n", x.load());
	return 0;
}

在这里插入图片描述


condition_variable 条件变量

C++11 中的 condition_variable 是用于 线程同步 的一种机制,它能够协调多个线程之间的操作,以便它们能够有效地进行通信和同步。

condition_variable 通常与互斥锁一起使用,用于实现生产者-消费者模型、读者-写者模型等线程间同步的场景。

condition_variable 提供了两个主要的操作:waitnotify_onenotify_all

  • wait 操作会使当前线程阻塞,并释放关联的互斥锁,直到另外一个线程调用了 notify_one 或 notify_all 方法,通知该线程可以继续执行了。

  • notify_one 操作会唤醒一个正在等待的线程,而notify_all 操作会唤醒所有正在等待的线程。如果没有线程处于等待状态,则这两个函数不会产生任何影响。

wait函数提供了两个不同版本的接口:

在这里插入图片描述

  • 调用第一个版本的wait函数时只需要传入一个互斥锁,线程调用wait后会立即被阻塞,直到被唤醒。

  • 调用第二个版本的wait函数时除了需要传入一个互斥锁,还需要传入一个返回值类型为bool的可调用对象,与第一个版本的wait不同的是,当线程被唤醒后还需要调用传入的可调用对象,如果可调用对象的返回值为false,那么该线程还需要继续被阻塞。

为什么调用wait系列函数时需要传入一个互斥锁?

  1. 因为wait系列函数一般是在临界区中调用的,为了让当前线程调用wait阻塞时其他线程能够获取到锁,因此调用wait系列函数时需要传入一个互斥锁,当线程被阻塞时这个互斥锁会被自动解锁,而当这个线程被唤醒时,又会自动获得这个互斥锁。

  2. 因此wait系列函数实际上有两个功能,一个是让线程在条件不满足时进行阻塞等待,另一个是让线程将对应的互斥锁进行解锁。

注意:调用wait系列的函数时,传入互斥锁的类型必须是unique_lock。条件变量下,可能会有多个线程正在进行阻塞等待,这些线程会被放到一个等待队列中进行排队。


实现两个线程交替打印1-100

尝试用两个线程交替打印1-100的数字,要求一个线程打印奇数,一个线程打印偶数,并且打印数字从小到大依次递增。

该题目主要考察的就是线程的同步和互斥。

  • 互斥:两个线程都在向控制台打印数据,为了保证两个线程的打印数据不会相互影响,因此需要对线程的打印过程进行加锁保护。
  • 同步:两个线程必须交替进行打印,因此需要用到条件变量让两个线程进行同步,当一个线程打印完再唤醒另一个线程进行打印。

但如果只有同步和互斥是无法满足题目要求的。

  1. 首先,我们无法保证哪一个线程会先进行打印,不能说先创建的线程就一定先打印,后创建的线程先打印也是有可能的。
  2. 此外,有可能会出现某个线程连续多次打印的情况,比如线程1先创建并打印了一个数字,当线程1准备打印第二个数字的时候线程2可能还没有创建出来,或是线程2还没有在互斥锁上进行等待,这时线程1就会再次获取到锁进行打印。
int main()
{
	mutex mtx;
	condition_variable cv;

	int n = 100;
	int x = 1;

	thread t1([&, n]() {
		while (x < 100)
		{
			unique_lock<mutex> lock(mtx);
			if (x >= 100) break;
			if (x % 2 == 0) { // 偶数就阻塞
				cv.wait(lock);
			}
			cout << "线程" << this_thread::get_id() << ":" << x << endl;
			++x;
			cv.notify_one();
		}
		});

	thread t2([&, n]() {
		while (x < 100)
		{
			unique_lock<mutex> lock(mtx);
			if (x > 100) break;
			if (x % 2 != 0) { // 奇数就阻塞
				cv.wait(lock);
			}
			cout << "线程" << this_thread::get_id() << ":" << x << endl;
			++x;
			cv.notify_one();
		}
		});

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

	return 0;
}

在这里插入图片描述


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

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

相关文章

Spring Framework 简介与起源

Spring是用于企业Java应用程序开发的最流行的应用程序开发框架。全球数百万开发人员使用Spring Framework创建高性能、易于测试和可重用的代码。 Spring Framework是一个开源的Java平台。它最初由Rod Johnson编写&#xff0c;并于2003年6月在Apache 2.0许可下首次发布。 Spri…

7 款最好的 Android 手机数据恢复软件榜单(持续更新列表)

数据丢失会干扰您的个人生活和业务&#xff0c;如果手动完成&#xff0c;可能很难恢复丢失的数据。 Android数据恢复软件是克服此问题的完美解决方案。 这些工具可以帮助您快速轻松地从Android设备恢复丢失的数据。 它可以帮助您恢复照片、视频、笔记、联系人等。 7 款最好的An…

双十一买高画质投影仪,当贝F6还是极米H6?

如果你想购买一台4K画质的投影仪,那么在各大平台搜索“双十一最值得买的4K投影仪”时,一定会注意到当贝F6和极米H6这两个型号投影仪。个人认为当贝F6和极米H6都分别是当贝和极米两个品牌非常具有性价比的4K投影仪。那么到底哪一台更适合你。 首先放一张参数对比图,方便参数控研…

有效数字(表示数值的字符串),剑指offer,力扣

目录 题目地址&#xff1a; 我们直接看题解吧&#xff1a; 难度分析&#xff1a; 解题方法&#xff1a; 审题目事例提示&#xff1a; 解题思路&#xff1a; 代码实现&#xff1a; 题目地址&#xff1a; LCR 138. 有效数字 - 力扣&#xff08;LeetCode&#xff09; 难度&#xf…

Win11系统安装或执行程序时提示:文件系统错误(-1073740771)解决方案

有用户反映&#xff0c;exe文件无法执行或者无法安装&#xff0c;报错如图所示&#xff1a; 解决方法&#xff1a; 方法一&#xff1a; 1.打开控制面板&#xff0c;可以采用”搜索“→”控制面板“的方式 2.控制面板选择“用户账户”&#xff0c;再选择“更改用户账户控制设…

Java-绘图

文章目录 Java绘图Java绘图类绘图颜色与画笔属性设置颜色设置画笔 绘制文本显示图片图像处理1、放大与缩小2、图像翻转3、图像旋转4、图像倾斜 End Java绘图 Java绘图是指在Java程序中创建和显示图形的过程。Java提供了许多类和方法来支持绘图。 Java绘图类 Java中主要的绘图类…

DevExpress WinForms HeatMap组件,一个高度可自定义热图控件!

通过DevExpress WinForms可以为Windows Forms桌面平台提供的高度可定制的热图UI组件&#xff0c;体验DevExpress的不同之处。 DevExpress WinForms有180组件和UI库&#xff0c;能为Windows Forms平台创建具有影响力的业务解决方案。同时能完美构建流畅、美观且易于使用的应用程…

云课五分钟-04一段代码学习-大模型分析C++

前篇&#xff1a; 云课五分钟-03第一个开源游戏复现-贪吃蛇 经过01-03&#xff0c;基本了解云课最大的优势之一就是快速复现&#xff08;部署&#xff09;。 视频&#xff1a; 云课五分钟-04一段代码学习-大模型分析C AIGC大模型时代&#xff0c;学习编程语言的方式&#xf…

知虾数据分析软件:了解知虾数据分析软件提升Shopee店铺运营效果

在如今电商竞争激烈的市场中&#xff0c;了解市场趋势和产品数据是成功经营一家Shopee店铺的重要因素之一。而知虾——Shopee生意参谋作为一款功能强大的数据分析软件&#xff0c;可以帮助店主深入了解行业概况、产品潜力以及市场趋势&#xff0c;从而制定最优的运营策略。本文…

NI USRP RIO软件无线电

NI USRP RIO软件无线电 NI USRP RIO是SDR游戏规则的改变者&#xff0c;它为无线通信设计人员提供了经济实惠的SDR和前所不高的性能&#xff0c;可帮助开发下一代5G无线通信系统。“USRP RIO”是一个术语&#xff0c;用于描述包含FPGA的USRP软件定义无线电设备&#xff0c;例如…

PC端微信@所有人逻辑漏洞

&#xff08;一&#xff09;过程 这个漏洞是PC端微信&#xff0c;可以越权让非管理员艾特所有人&#xff0c;具体步骤如下 第一步&#xff1a;找一个自己的群&#xff08;要有艾特所有人的权限&#xff09;“123”是我随便输入的内容&#xff0c;可以更改&#xff0c;然后按c…

技巧篇:Mac 环境PyCharm 配置 python Anaconda

Mac 中 PyCharm 配置 python Anaconda环境 在 python 开发中我们最常用的IDE就是PyCharm&#xff0c;有关PyCharm的优点这里就不在赘述。在项目开发中我们经常用到许多第三方库&#xff0c;用的最多的命令就是pip install 第三方库名 进行安装。现在你可以使用一个工具来帮你解…

超全面测评!国内有多少软件能替代Axure?

从事产品工作至今&#xff0c;我使用Axure已有8年了。这数年时间里&#xff0c;我从产品小白到带领产品团队&#xff0c;Axure的确对我的工作提供了太多的支持&#xff0c;由于Axure预设组件太少、交互设置复杂、团队协作不方便、学习成本较高等原因&#xff0c;公司要我寻找一…

用嘉立创查找元件的原理图

目录 1.打开立创商城 2.搜索元件 ​编辑 3.复制编号 ​编辑 4.打开元件库 5.将复制好的编号进行搜索 1.打开立创商城 2.搜索元件 3.复制编号 4.打开元件库 5.将复制好的编号进行搜索

【halcon】踩坑unin2

unin1 先说 unin1 这个函数 很好理解&#xff0c;将多个区域求并集&#xff0c;结果就是多个区域会变成一个区域。 unin1 经常和 closing_circle 这个函数连用。 这是应为 当多个区域变成一个区域之后&#xff0c;才能更好的完成闭操作。 unin2 按照思维惯性&#xff0c;看上…

ssh脚本找不到命令或者执行无效的解决办法

如图&#xff1a;今天在编写脚本时发现的这个问题&#xff0c; 在排除脚本语法错误、编码格式等情况下&#xff0c;仍然出现“bash 。。未找到命令”的字样 解决办法&#xff1a; 给每台虚拟机的环境变量source一下&#xff1a; 命令如下 source /etc/profile或者输入 vim ~…

2023年05月 Python(五级)真题解析#中国电子学会#全国青少年软件编程等级考试

Python等级考试(1~6级)全部真题・点这里 一、单选题(共25题,每题2分,共50分) 第1题 有列表L=[‘UK’,‘china’,‘lili’,“张三”],print(L[-2])的结果是?( ) A: UK B: ‘lili’,‘张三’ C: lili D: ‘UK’,‘china’,‘lili’ 答案:C 列表元素定位 第2题 …

乌卡时代确定性稀缺,企业多云战略最需看中什么?

双十一刚过&#xff0c;云服务商宕机导致大面积故障的新闻就引爆全网&#xff0c;再一次引发全网关于云计算安全可靠的大讨论。 在一个充满着复杂性、不稳定性和不确定性的乌卡时代&#xff0c;云故障频发似乎已发展成一种“常态”&#xff0c;让企业对于云计算逐渐产生诸多顾…

利用Nextcloud搭建企业私有云盘系统

利用Nextcloud搭建企业私有云盘系统 1. 场景介绍2. 环境准备3. 安装NextCloud4. 系统功能验证 1. 场景介绍 Nextcloud是一款免费开源的私有云存储系统&#xff0c;采用PHPMySQL开发&#xff0c;提供了多个同步客户端支持多种设备访问&#xff0c;使用Nextcloud可以快速便捷地搭…

深度学习LSTM新冠数据预测 计算机竞赛

文章目录 0 前言1 课题简介2 预测算法2.1 Logistic回归模型2.2 基于动力学SEIR模型改进的SEITR模型2.3 LSTM神经网络模型 3 预测效果3.1 Logistic回归模型3.2 SEITR模型3.3 LSTM神经网络模型 4 结论5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 …
最新文章