C++11 并发指南四( 详解三 stdfuture stdshared_future)

news2024/9/24 3:29:04

C++11 并发指南四( 详解三 std::future & std::shared_future)

文章目录

上一讲《C++11 并发指南四( 详解二 std::packaged_task 介绍)》主要介绍了 头文件中的 std::packaged_task 类,本文主要介绍 std::future,std::shared_future 以及 std::future_error,另外还会介绍 头文件中的 std::async,std::future_category 函数以及相关枚举类型。

std::future 介绍

前面已经多次提到过 std::future,那么 std::future 究竟是什么呢?简单地说,std::future 可以用来获取异步任务的结果,因此可以把它当成一种简单的线程间同步的手段。std::future 通常由某个 Provider 创建,你可以把 Provider 想象成一个异步任务的提供者,Provider 在某个线程中设置共享状态的值,与该共享状态相关联的 std::future 对象调用 get(通常在另外一个线程中) 获取该值,如果共享状态的标志不为 ready,则调用 std::future::get 会阻塞当前的调用者,直到 Provider 设置了共享状态的值(此时共享状态的标志变为 ready),std::future::get 返回异步任务的值或异常(如果发生了异常)。

一个有效(valid)的 std::future 对象通常由以下三种 Provider 创建,并和某个共享状态相关联。Provider 可以是函数或者类,其实我们前面都已经提到了,他们分别是:

  • std::async 函数,本文后面会介绍 std::async() 函数。
  • std::promise::get_future,get_future 为 promise 类的成员函数,详见 C++11 并发指南四( 详解一 std::promise 介绍)。
  • std::packaged_task::get_future,此时 get_future为 packaged_task 的成员函数,详见C++11 并发指南四( 详解二 std::packaged_task 介绍)。

一个 std::future 对象只有在有效(valid)的情况下才有用(useful),由 std::future 默认构造函数创建的 future 对象不是有效的(除非当前非有效的 future 对象被 move 赋值另一个有效的 future 对象)。

在一个有效的 future 对象上调用 get 会阻塞当前的调用者,直到 Provider 设置了共享状态的值或异常(此时共享状态的标志变为 ready),std::future::get 将返回异步任务的值或异常(如果发生了异常)。

下面以一个简单的例子说明上面一段文字吧(参考):

[复制代码](javascript:void(0)😉

// future example
#include <iostream>             // std::cout
#include <future>               // std::async, std::future
#include <chrono>               // std::chrono::milliseconds

// a non-optimized way of checking for prime numbers:
bool
is_prime(int x)
{
    for (int i = 2; i < x; ++i)
        if (x % i == 0)
            return false;
    return true;
}

int
main()
{
    // call function asynchronously:
    std::future < bool > fut = std::async(is_prime, 444444443);

    // do something while waiting for function to set future:
    std::cout << "checking, please wait";
    std::chrono::milliseconds span(100);
    while (fut.wait_for(span) == std::future_status::timeout)
        std::cout << '.';

    bool x = fut.get();         // retrieve return value

    std::cout << "\n444444443 " << (x ? "is" : "is not") << " prime.\n";

    return 0;
}

[复制代码](javascript:void(0)😉

std::future 成员函数

std::future 构造函数

std::future 一般由 std::async, std::promise::get_future, std::packaged_task::get_future 创建,不过也提供了构造函数,如下表所示:

default (1)future() noexcept;
copy [deleted] (2)future (const future&) = delete;
move (3)future (future&& x) noexcept;

不过 std::future 的拷贝构造函数是被禁用的,只提供了默认的构造函数和 move 构造函数(注:C++ 新特新)。另外,std::future 的普通赋值操作也被禁用,只提供了 move 赋值操作。如下代码所示:

 std::future<int> fut;           // 默认构造函数
  fut = std::async(do_some_task);   // move-赋值操作。

std::future::share()

返回一个 std::shared_future 对象(本文后续内容将介绍 std::shared_future ),调用该函数之后,该 std::future 对象本身已经不和任何共享状态相关联,因此该 std::future 的状态不再是 valid 的了。

[复制代码](javascript:void(0)😉

#include <iostream>       // std::cout
#include <future>         // std::async, std::future, std::shared_future

int do_get_value() { return 10; }

int main ()
{
    std::future<int> fut = std::async(do_get_value);
    std::shared_future<int> shared_fut = fut.share();

    // 共享的 future 对象可以被多次访问.
    std::cout << "value: " << shared_fut.get() << '\n';
    std::cout << "its double: " << shared_fut.get()*2 << '\n';

    return 0;
}

[复制代码](javascript:void(0)😉

std::future::get()

std::future::get 一共有三种形式,如下表所示(参考):

generic template (1)T get();
reference specialization (2)R& future<R&>::get(); // when T is a reference type (R&)
void specialization (3)void future<void>::get(); // when T is void

当与该 std::future 对象相关联的共享状态标志变为 ready 后,调用该函数将返回保存在共享状态中的值,如果共享状态的标志不为 ready,则调用该函数会阻塞当前的调用者,而此后一旦共享状态的标志变为 ready,get 返回 Provider 所设置的共享状态的值或者异常(如果抛出了异常)。

请看下面的程序:

[复制代码](javascript:void(0)😉

#include <iostream>       // std::cin, std::cout, std::ios
#include <functional>     // std::ref
#include <thread>         // std::thread
#include <future>         // std::promise, std::future
#include <exception>      // std::exception, std::current_exception

void get_int(std::promise<int>& prom) {
    int x;
    std::cout << "Please, enter an integer value: ";
    std::cin.exceptions (std::ios::failbit);   // throw on failbit
    try {
        std::cin >> x;                         // sets failbit if input is not int
        prom.set_value(x);
    } catch (std::exception&) {
        prom.set_exception(std::current_exception());
    }
}

void print_int(std::future<int>& fut) {
    try {
        int x = fut.get();
        std::cout << "value: " << x << '\n';
    } catch (std::exception& e) {
        std::cout << "[exception caught: " << e.what() << "]\n";
    }
}

int main ()
{
    std::promise<int> prom;
    std::future<int> fut = prom.get_future();

    std::thread th1(get_int, std::ref(prom));
    std::thread th2(print_int, std::ref(fut));

    th1.join();
    th2.join();
    return 0;
}

[复制代码](javascript:void(0)😉

std::future::valid()

检查当前的 std::future 对象是否有效,即释放与某个共享状态相关联。一个有效的 std::future 对象只能通过 std::async(), std::future::get_future 或者 std::packaged_task::get_future 来初始化。另外由 std::future 默认构造函数创建的 std::future 对象是无效(invalid)的,当然通过 std::future 的 move 赋值后该 std::future 对象也可以变为 valid。

[复制代码](javascript:void(0)😉

#include <iostream>       // std::cout
#include <future>         // std::async, std::future
#include <utility>        // std::move

int do_get_value() { return 11; }

int main ()
{
    // 由默认构造函数创建的 std::future 对象,
    // 初始化时该 std::future 对象处于为 invalid 状态.
    std::future<int> foo, bar;
    foo = std::async(do_get_value); // move 赋值, foo 变为 valid.
    bar = std::move(foo); // move 赋值, bar 变为 valid, 而 move 赋值以后 foo 变为 invalid.

    if (foo.valid())
        std::cout << "foo's value: " << foo.get() << '\n';
    else
        std::cout << "foo is not valid\n";

    if (bar.valid())
        std::cout << "bar's value: " << bar.get() << '\n';
    else
        std::cout << "bar is not valid\n";

    return 0;
}

[复制代码](javascript:void(0)😉

std::future::wait()

等待与当前std::future 对象相关联的共享状态的标志变为 ready.

如果共享状态的标志不是 ready(此时 Provider 没有在共享状态上设置值(或者异常)),调用该函数会被阻塞当前线程,直到共享状态的标志变为 ready。
一旦共享状态的标志变为 ready,wait() 函数返回,当前线程被解除阻塞,但是 wait() 并不读取共享状态的值或者异常。下面的代码说明了 std::future::wait() 的用法(参考)

[复制代码](javascript:void(0)😉

#include <iostream>                // std::cout
#include <future>                // std::async, std::future
#include <chrono>                // std::chrono::milliseconds

// a non-optimized way of checking for prime numbers:
bool do_check_prime(int x) // 为了体现效果, 该函数故意没有优化.
{
    for (int i = 2; i < x; ++i)
        if (x % i == 0)
            return false;
    return true;
}

int main()
{
    // call function asynchronously:
    std::future < bool > fut = std::async(do_check_prime, 194232491);

    std::cout << "Checking...\n";
    fut.wait();

    std::cout << "\n194232491 ";
    if (fut.get()) // guaranteed to be ready (and not block) after wait returns
        std::cout << "is prime.\n";
    else
        std::cout << "is not prime.\n";

    return 0;
}

[复制代码](javascript:void(0)😉

执行结果如下:

concurrency ) ./Future-wait 
Checking...

194232491 is prime.
concurrency ) 

std::future::wait_for()

与 std::future::wait() 的功能类似,即等待与该 std::future 对象相关联的共享状态的标志变为 ready,该函数原型如下:

template <class Rep, class Period>
  future_status wait_for (const chrono::duration<Rep,Period>& rel_time) const;

而与 std::future::wait() 不同的是,wait_for() 可以设置一个时间段 rel_time,如果共享状态的标志在该时间段结束之前没有被 Provider 设置为 ready,则调用 wait_for 的线程被阻塞,在等待了 rel_time 的时间长度后 wait_until() 返回,返回值如下:

返回值描述
future_status::ready共享状态的标志已经变为 ready,即 Provider 在共享状态上设置了值或者异常。
future_status::timeout超时,即在规定的时间内共享状态的标志没有变为 ready。
future_status::deferred共享状态包含一个 deferred 函数。

请看下面的例子:

[复制代码](javascript:void(0)😉

#include <iostream>                // std::cout
#include <future>                // std::async, std::future
#include <chrono>                // std::chrono::milliseconds

// a non-optimized way of checking for prime numbers:
bool do_check_prime(int x) // 为了体现效果, 该函数故意没有优化.
{
    for (int i = 2; i < x; ++i)
        if (x % i == 0)
            return false;
    return true;
}

int main()
{
    // call function asynchronously:
    std::future < bool > fut = std::async(do_check_prime, 194232491);

    std::cout << "Checking...\n";
    std::chrono::milliseconds span(1000); // 设置超时间隔.

    // 如果超时,则输出".",继续等待
    while (fut.wait_for(span) == std::future_status::timeout)
        std::cout << '.';

    std::cout << "\n194232491 ";
    if (fut.get()) // guaranteed to be ready (and not block) after wait returns
        std::cout << "is prime.\n";
    else
        std::cout << "is not prime.\n";

    return 0;
}

[复制代码](javascript:void(0)😉

std::future::wait_until()

与 std::future::wait() 的功能类似,即等待与该 std::future 对象相关联的共享状态的标志变为 ready,该函数原型如下:

template <class Rep, class Period>
  future_status wait_until (const chrono::time_point<Clock,Duration>& abs_time) const;

而 与 std::future::wait() 不同的是,wait_until() 可以设置一个系统绝对时间点 abs_time,如果共享状态的标志在该时间点到来之前没有被 Provider 设置为 ready,则调用 wait_until 的线程被阻塞,在 abs_time 这一时刻到来之后 wait_for() 返回,返回值如下:

返回值描述
future_status::ready共享状态的标志已经变为 ready,即 Provider 在共享状态上设置了值或者异常。
future_status::timeout超时,即在规定的时间内共享状态的标志没有变为 ready。
future_status::deferred共享状态包含一个 deferred 函数。

std::shared_future 介绍

std::shared_future 与 std::future 类似,但是 std::shared_future 可以拷贝、多个 std::shared_future 可以共享某个共享状态的最终结果(即共享状态的某个值或者异常)。shared_future 可以通过某个 std::future 对象隐式转换(参见 std::shared_future 的构造函数),或者通过 std::future::share() 显示转换,无论哪种转换,被转换的那个 std::future 对象都会变为 not-valid.

std::shared_future 构造函数

std::shared_future 共有四种构造函数,如下表所示:

default (1)shared_future() noexcept;
copy (2)shared_future (const shared_future& x);
move (3)shared_future (shared_future&& x) noexcept;
move from future (4)shared_future (future<T>&& x) noexcept;

最后 move from future(4) 即从一个有效的 std::future 对象构造一个 std::shared_future,构造之后 std::future 对象 x 变为无效(not-valid)。

std::shared_future 其他成员函数

std::shared_future 的成员函数和 std::future 大部分相同,如下(每个成员函数都给出了连接):

  • operator=

    赋值操作符,与 std::future 的赋值操作不同,std::shared_future 除了支持 move 赋值操作外,还支持普通的赋值操作。

  • get

    获取与该 std::shared_future 对象相关联的共享状态的值(或者异常)。

  • valid

    有效性检查。

  • wait

    等待与该 std::shared_future 对象相关联的共享状态的标志变为 ready。

  • wait_for

    等待与该 std::shared_future 对象相关联的共享状态的标志变为 ready。(等待一段时间,超过该时间段wait_for 返回。)

  • wait_until

    等待与该 std::shared_future 对象相关联的共享状态的标志变为 ready。(在某一时刻前等待,超过该时刻 wait_until 返回。)

std::future_error 介绍

class future_error : public logic_error;

std::future_error 继承子 C++ 标准异常体系中的 logic_error,有关 C++ 异常的继承体系,请参考相关的C++教程 😉。

其他与 std::future 相关的函数介绍

与 std::future 相关的函数主要是 std::async(),原型如下:

unspecified policy (1)template <class Fn, class... Args> future<typename result_of<Fn(Args...)>::type> async(Fn&& fn, Args&&... args);
specific policy (2)template <class Fn, class... Args> future<typename result_of<Fn(Args...)>::type> async(launch policy, Fn&& fn, Args&&... args);

上面两组 std::async() 的不同之处是第一类 std::async 没有指定异步任务(即执行某一函数)的启动策略(launch policy),而第二类函数指定了启动策略,详见 std::launch 枚举类型,指定启动策略的函数的 policy 参数可以是launch::async,launch::deferred,以及两者的按位或( | )。

std::async() 的 fn 和 args 参数用来指定异步任务及其参数。另外,std::async() 返回一个 std::future 对象,通过该对象可以获取异步任务的值或异常(如果异步任务抛出了异常)。

下面介绍一下 std::async 的用法。

[复制代码](javascript:void(0)😉

#include <stdio.h>
#include <stdlib.h>

#include <cmath>
#include <chrono>
#include <future>
#include <iostream>

double ThreadTask(int n) {
    std::cout << std::this_thread::get_id()
        << " start computing..." << std::endl;

    double ret = 0;
    for (int i = 0; i <= n; i++) {
        ret += std::sin(i);
    }

    std::cout << std::this_thread::get_id()
        << " finished computing..." << std::endl;
    return ret;
}

int main(int argc, const char *argv[])
{
    std::future<double> f(std::async(std::launch::async, ThreadTask, 100000000));

#if 0
    while(f.wait_until(std::chrono::system_clock::now() + std::chrono::seconds(1))
            != std::future_status::ready) {
        std::cout << "task is running...\n";
    }
#else
    while(f.wait_for(std::chrono::seconds(1))
            != std::future_status::ready) {
        std::cout << "task is running...\n";
    }
#endif

    std::cout << f.get() << std::endl;

    return EXIT_SUCCESS;
}

[复制代码](javascript:void(0)😉

其他与 std::future 相关的枚举类介绍

下面介绍与 std::future 相关的枚举类型。与 std::future 相关的枚举类型包括:

enum class future_errc;
enum class future_status;
enum class launch;

下面分别介绍以上三种枚举类型:

std::future_errc 类型

std::future_errc 类型描述如下(参考):

类型取值描述
broken_promise0与该 std::future 共享状态相关联的 std::promise 对象在设置值或者异常之前一被销毁。
future_already_retrieved1与该 std::future 对象相关联的共享状态的值已经被当前 Provider 获取了,即调用了 std::future::get 函数。
promise_already_satisfied2std::promise 对象已经对共享状态设置了某一值或者异常。
no_state3无共享状态。

std::future_status 类型(参考)

std::future_status 类型主要用在 std::future(或std::shared_future)中的 wait_for 和 wait_until 两个函数中的。

类型取值描述
future_status::ready0wait_for(或wait_until) 因为共享状态的标志变为 ready 而返回。
future_status::timeout1超时,即 wait_for(或wait_until) 因为在指定的时间段(或时刻)内共享状态的标志依然没有变为 ready 而返回。
future_status::deferred2共享状态包含了 deferred 函数。

std::launch 类型

该枚举类型主要是在调用 std::async 设置异步任务的启动策略的。**
**

类型描述
launch::asyncAsynchronous: 异步任务会在另外一个线程中调用,并通过共享状态返回异步任务的结果(一般是调用 std::future::get() 获取异步任务的结果)。
launch::deferredDeferred: 异步任务将会在共享状态被访问时调用,相当与按需调用(即延迟(deferred)调用)。

请看下例(参考):

[复制代码](javascript:void(0)😉

#include <iostream>                // std::cout
#include <future>                // std::async, std::future, std::launch
#include <chrono>                // std::chrono::milliseconds
#include <thread>                // std::this_thread::sleep_for

void
do_print_ten(char c, int ms)
{
    for (int i = 0; i < 10; ++i) {
        std::this_thread::sleep_for(std::chrono::milliseconds(ms));
        std::cout << c;
    }
}

int
main()
{
    std::cout << "with launch::async:\n";
    std::future < void >foo =
        std::async(std::launch::async, do_print_ten, '*', 100);
    std::future < void >bar =
        std::async(std::launch::async, do_print_ten, '@', 200);
    // async "get" (wait for foo and bar to be ready):
    foo.get();
    bar.get();
    std::cout << "\n\n";

    std::cout << "with launch::deferred:\n";
    foo = std::async(std::launch::deferred, do_print_ten, '*', 100);
    bar = std::async(std::launch::deferred, do_print_ten, '@', 200);
    // deferred "get" (perform the actual calls):
    foo.get();
    bar.get();
    std::cout << '\n';

    return 0;
}

[复制代码](javascript:void(0)😉

在我的机器上执行结果:

with launch::async:
*@**@**@**@**@*@@@@@

with launch::deferred:
**********@@@@@@@@@@

bar.get();
std::cout << “\n\n”;

std::cout << "with launch::deferred:\n";
foo = std::async(std::launch::deferred, do_print_ten, '*', 100);
bar = std::async(std::launch::deferred, do_print_ten, '@', 200);
// deferred "get" (perform the actual calls):
foo.get();
bar.get();
std::cout << '\n';

return 0;

}


[[外链图片转存中...(img-eE6GGkx5-1674832496916)]](javascript:void(0);)

在我的机器上执行结果:

with launch::async:
@@@@@@@@@@

with launch::deferred:
**********@@@@@@@@@@


 



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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SWIFT Framework .NET 2023.1 Crack

SWIFT 组件 SWIFT Components 多年来一直致力于银行机构软件、在线交易软件、CRM 和零售和商业解决方案中的计费应用程序&#xff0c;以及医药和高度关键任务的 24/7 运营应用程序。作为定制银行解决方案的开发商&#xff0c;SWIFT Components 拥有所有银行产品和部门的专业知识…

【python从零入门 | 03】基本数据类型之“除法“

🍁博主简介: 🏅云计算领域优质创作者 🏅2022年CSDN新星计划python赛道第一名 🏅2022年CSDN原力计划优质作者 🏅阿里云ACE认证高级工程师 🏅阿里云开发者社区专家博主 💊交流社区:CSDN云计算交流社区欢迎您的加入! 目

设置/恢复系统隐藏文件 - Windows系统

设置/恢复系统隐藏文件 - Windows系统前言普通隐藏文件显示文件设置/取消隐藏系统隐藏文件显示文件设置/取消隐藏前言 本文介绍Windows系统如何设置/恢复隐藏文件&#xff0c;隐藏文件包含普通隐藏文件和系统隐藏文件&#xff0c;系统隐藏文件一般是受保护的系统文件&#xff…

Android 蓝牙开发——Avrcp协议(十二)

SDK路径&#xff1a;frameworks/base/core/java/android/bluetooth/ 服务路径&#xff1a;packages/apps/Bluetooth/src/com/android/bluetooth/ 在使用协议类的时候无法找到该类&#xff0c;由于安卓源码中关于蓝牙协议的 Client 部分或相关接口都被 hide 给隐藏掉了&#xf…

【通信原理(含matlab程序)】实验一 双边带模拟调制和解调

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; 本人持续分享更多关于电子通信专业内容以及嵌入式和单片机的知识&#xff0c;如果大家喜欢&#xff0c;别忘点个赞加个关注哦&#xff0c;让我们一起共同进步~ &#x…

AcWing - 寒假每日一题2023(DAY 11——DAY 15)

文章目录一、AcWing 4656. 技能升级&#xff08;困难&#xff09;1. 实现思路2. 实现代码二、AcWing 4454. 未初始化警告&#xff08;简单&#xff09;1. 实现思路2. 实现代码三、AcWing 4509. 归一化处理&#xff08;简单&#xff09;1. 实现思路2. 实现代码四、AcWing 4699. …

OpenCV实战(8)——直方图详解

OpenCV实战&#xff08;8&#xff09;——直方图详解0. 前言1. 直方图概念2. 直方图计算2.1 灰度图像直方图计算2.2 彩色图像直方图计算3. 应用查找表修改图像3.1 查找表3.2 拉伸直方图提高图像对比度3.3 在彩色图像上应用查找表4. 图像直方图均衡化5. 完整代码小结系列链接0. …

操作流程违规作业监测系统 yolov7

操作流程违规作业监测系统通过pythonyolov7网络深度学习技术&#xff0c;对高危场景下作业人员未按照操作流程进行正常操作行为进行实时分析识别检测&#xff0c;发现现场人员违规作业操作行为&#xff0c;不需人为干预&#xff0c;立即自动抓拍存档预警。YOLOv7 在 5 FPS 到 1…

在 VSCode 中像写 TypeScript 一样写 JavaScript

大家好&#xff0c;我是前端西瓜哥。 我们在 VSCode 编辑器中编写 js 代码&#xff0c;是会提供类型提示的。 VSCode 会推断一个变量是什么类型&#xff0c;并在你输入内容的时候&#xff0c;提供对应的 API 属性或方法补全。 如下图&#xff0c;在 js 文件中&#xff0c;ar…

【Java】到底什么是包?|最通俗易懂讲解|保姆级

博主简介&#xff1a;努力学习的预备程序媛一枚~博主主页&#xff1a; 是瑶瑶子啦所属专栏: Java岛冒险记【从小白到大佬之路】 目录Part1&#xff1a;类比理解&#xff1a;Part2&#xff1a;与包&#xff08;package)正式见面&#xff1a;2.1&#xff1a;包的本质--文件夹2.2&…

学习C++基本数值类型

写在前面 正在学习C/C/Javascript&#xff0c;面向初学者撰写专栏 博主原创C/C笔记&#xff08;干货&#xff09;&#xff0c;如有错误之处请各位读者指正 请读者评论回复、参与投票&#xff0c;反馈给作者&#xff0c;我会获得持续更新各类干货的动力。 致粉丝&#xff1a;可以…

力扣刷题记录——709. 转换成小写字母、771. 宝石与石头、704. 二分查找

本专栏主要记录力扣的刷题记录&#xff0c;备战蓝桥杯&#xff0c;供复盘和优化算法使用&#xff0c;也希望给大家带来帮助&#xff0c;博主是算法小白&#xff0c;希望各位大佬不要见笑&#xff0c;今天要分享的是——《力扣刷题记录——709. 转换成小写字母、771. 宝石与石头…

C++11并发指南二(stdthread详解)

C11并发指南二&#xff08;stdthread详解&#xff09; 文章目录C11并发指南二&#xff08;stdthread详解&#xff09;std::thread 构造move 赋值操作其他成员函数上一篇博客《 C11 并发指南一(C11 多线程初探)》中只是提到了 std::thread 的基本用法&#xff0c;并给出了一个最…

{(leetcode 题号:169. 多数元素)+(189. 轮转数组)}时间复杂度与空间复杂度分析:

目录 一. 基本概念 1.时间复杂度 2.空间复杂度 二.实例分析 实例(1):旋转数组 方法1:暴力旋转法(时间复杂度加空间复杂度分析) 方法2 :三步整体逆序法 (时间复杂度加空间复杂度分析) 实例(2):斐波那契递归的时间复杂度和空间复杂度分析 实例(3)&#xff1a;169. 多数元素…

模拟实现C库函数(1)

"啊~所有经历给它赋予魔力。"很久没更新过C专栏的文章了&#xff0c;借复习(review)的机会&#xff0c;本节的内容针对我们一些常见、常用的C库函数的模拟实现。“当你行走了一段时间后&#xff0c;回头往往那不管是起初咿咿呀呀胡乱踩陷的小坑时&#xff0c;还是之后…

C++11并发指南三(stdmutex详解)

C11并发指南三&#xff08;std:mutex详解&#xff09; 文章目录C11并发指南三&#xff08;std:mutex详解&#xff09;<mutex> 头文件介绍Mutex 系列类(四种)Lock 类&#xff08;两种&#xff09;其他类型函数std::mutex 介绍std::mutex 的成员函数std::recursive_mutex 介…

miracl

文章目录Windows平台编译网址 https://miracl.com/https://github.com/miracl/MIRACL Windows平台编译 源码目录下新建文件夹ms32或ms64&#xff0c;把/lib/ms32doit.bat或ms64doit.bat分别拷进去。 把源码include和source目录所有文件拷贝进要编译的ms32或ms64&#xff0c…

32. 实战:PyQuery实现抓取TX图文新闻

目录 前言 &#xff08;链接在评论区&#xff09;&#xff08;链接在评论区&#xff09;&#xff08;链接在评论区&#xff09; 目的 &#xff08;链接在评论区&#xff09;&#xff08;链接在评论区&#xff09;&#xff08;链接在评论区&…

ATAC-seq分析:Motifs分析(11)

1. 切割位点 ATACseq 应该在较小的保护区&#xff08;如转录因子结合位点&#xff09;周围生成较短的片段&#xff08;我们的无核小体区域&#xff09;。 因此&#xff0c;我们可以在不同组织/细胞类型/样本中寻找围绕感兴趣基序的切割位点堆积。 为了从我们的 BAM 文件中生成切…

FecMall多语言商城宝塔安装搭建教程

FecMall多语言商城宝塔安装搭建教程 1.1、删除禁用函数 PHP管理→禁用函数&#xff0c;删除putenv、pcntl_signal函数 如果不删除会报错&#xff1a;[ErrorException] pcntl_signal() has been disabled for security reasons 1.2下载fecmall 进入如下目录中cd /www/wwwroot 下…