Git Product home page Git Product logo

blog's People

Contributors

congjiye avatar

Watchers

 avatar  avatar

blog's Issues

Github clone 速度慢

在 github.com 地址后加入 cnpmjs.org

例如

# 源地址
git clone https://github.com/libevent/libevent.git

# 修改后地址
git clone https://github.com.cnpmjs.org/libevent/libevent.git

修改前速度
image

修改后速度
image

python程序打包为pyd文件

python 把程序打包为 pyd 可以不仅可以提高程序的安全性还可以对程序的运行速度有一定的提升. pyd 代表的是 windows 平台下的动态库名称.

打包程序

将下方代码保存为 setup.py 文件.注意在 source_files 中不要添加 init.py 文件.

from distutils.core import setup
import os
from Cython.Build import cythonize

source_files = ["***.py"]

setup(
    name="设置名称",
    ext_modules=cythonize(source_files)
)

# python setup.py build_ext --inplace
print("--------", os.getcwd(), "--------")
files = os.listdir(os.getcwd())
print(files)

for root, dirs, files in os.walk('.'):
    for file in files:
        if file.endswith(".pyd"):
            re_name = file.split(".")[0] + ".pyd"
            print(re_name)
            os.rename(os.path.join(root, file), os.path.join(root, re_name))
        elif file.endswith(".c"):
            os.remove(os.path.join(root, file))

Makefile 教程

Make的概念

make 指令其实是根据用户指定的 shell 命令进行构建的工具。例如有如下的一个规则文件

a.txt: b.txt c.txt
        cat b.txt c.txt > a.txt

该文件的含义是构建一个 a.txt 文件。该文件依赖于 b.txt 和 c.txt 文件,再确定这两个文件存在时进行构建 a.txt 文件。构建的命令为 cat b.txt c.txt > a.txt。当用户执行 make a.txt 文件后会生成 a.txt 文件。

Makefile文件的格式

Makefile 文件由一系列规则构成。每条规则的形式如下所示

<target>: <prerequisites>
[tab] <commands>

注意 从第二行起的每一行必须有一个 tab 键起首(不能是四个空格),后面跟着命令。

target 是必须的。prerequisitescommands 都是可选的,但是二者必须存在一个。

每条规则所明确的就是两件事

  1. 构建目前的前置条件是什么
  2. 如何构建

目标 (target)

一个目标就构成一条规则。目标通常是文件名,指明 Make 命令所要构建的对象,比如上文中的 a.txt。目标可以是一个文件名,也可以是多个文件名,之间用空格分隔。

除了文件名,目标还可以是某个操作的名字,这称为 伪目标 (phony target)

clean:
    rm a.txt

执行 make clean 命令会发现之前生成的 a.txt 文件背成功删除了。

但是如果当前目录中正好有一个文件叫做 clean,那么这个命令不会执行。因为 Make 发现 clean 文件已经存在,就认为没有必要重新构建了,就不会执行指定的 rm 命令。

为了避免这种情况,可以明确声明 clean 是伪目标,写法如下。

.PHONY: clean
clean:
    rm a.txt

声明为伪目标后,make 就不会去检查是否存在一个叫做 clean 的文件,而是每次运行都执行对应的命令。像 .PHONY 这样的内置目标名还有不少,可以查看手册

如果 Make 命令运行时没有指定目标,默认会执行 Makefile 文件的第一个目标。

前置条件 (prerequisites)

前置条件通常时一组文件名,之间用空格分隔。它制定了 目标 是否重新构建的判断标准。只要有一个前置文件不存在,或者有过更新 (前置文件的 last-modification 时间戳比目标的时间戳新), 目标就需要重新构建。

result.txt: source.txt
    cp source.txt result.txt

构建 source.txt 文件的命令如下

source.txt:
    echo "this is the source" > source.txt

上面的文件中,source.txt 后面没有前置条件,就意味着他跟其他文件都无关,只要这个文件还不存在,每次调用 make source.txt 它都会生成。

make result.txt
make result.txt

上面的命令连续执行两次。第一次执行会新建 source.txt 然后再创建 result.txt。第二次执行 Make 发现 source.txt 文件没有变动,就不会执行任何操作,result.txt 也不会重新生成。

如果需要生成多个文件,往往采用下面的写法。

source: file1 file2 file3

上面代码中 source 是一个伪目标,只有三个前置条件,没有任何对应的命令。

make source

执行上述命令后,就会一次性生成 file1,file2,file3 三个文件,这比下面的方法要方便很多。

make file1
make file2
make file3

命令 (commands)

命令表示如何更新目标文件,由一行或多行的 shell 命令组成。它是构建目标的具体指令,它的运行结果通常就是生成目标文件。

每行命令之前必须要有一个 tab 键,如果想用其他键,可以用内置变量 .RECIPEPREFIX 声明

.RECIPEPREFIX = >
all:
> echo Hello, world

上面代码用 .RECIPEPREFIX 指定使用 > 替代 tab 键。所以,每一行命令的起首变成了大于号,而不是 tab 键。

需要注意的是,每行命令再一个单独的 shell 中执行。这些 shell 之间没有继承关系。

var-lost:
    export foo=bar
    echo "foo=[$$foo]"

上面代码执行后取不到 foo 的值,因为两行命令在两个不同的进程执行。一个解决办法是将两行命令写在一行,中间用分号分隔。

var-kept:
    export foo=bar;echo "foo=[$$foo]"

另一个方法是在换行符前加反斜杠转义。

var-kept:
    export foo=bar; \
    echo "foo=[$$foo]"

最后一个方法是加上 .ONESHELL: 命令

.ONESHELL:
var-kepy:
    export foo=bar;
    echo "foo=[$$foo]"

Makefile文件的语法

注释

井号 (#) 在 Makefile 中表示注释

# 这是注释
result.txt: source.txt
    cp source.txt result.txt # 这也是注释

回声 (echoing)

正常情况下,make 会打印每条命令,然后再执行,这就叫做回声

test:
    # 这是测试

执行上面的规则,会得到下面的结果。

make test
# 这是测试

在命令的前面加上 @,就可以关闭回声

test:
    @# 这是测试

现在再执行 make test 就不会有任何输出。

由于在构建过程中,需要了解当前在执行哪条命令,所以通常只在注释和纯显示的 echo 命令前面加上 @。

通配符

通配符 (wildcard) 用来指定一组符合条件的文件名。Makefile 的通配符与 Bash 一致,主要有星号 (*)、问号 (?)、和 [...]。比如 *.o 表示所有后缀名为 o 的文件。

clean:
    rm -f *.o

模式匹配

Make 命令允许对文件名进行类似正则运算的匹配,主要用到的匹配符是 %。比如,假定当前目录下有 f1.c 和 f2.c 两个源码文件,需要将他们编译为对应的对象文件。

%.o: %.c

等同于下面的写法

f1.o: f1.c
f2.o: f2.c

使用通配符可以将大量同类型的文件只用一条规则就完成构建。

变量和赋值符

Makefile 允许使用等号自定义变量。

txt = Hello World
test:
    @echo $(txt)

上面代码中,变量 txt 等于 Hello World。调用时,变量需要放在 $()

调用 shell 变量,需要在 $ 符号前再加一个 $ 符号,这是因为 Make 命令会对 $ 符号进行转义。

test:
    @echo $$HOME

有时,变量的值可能指向另一个变量。

v1 = $(v2)

上面代码中,变量 v1 的值是另一个变量 v2.这时会产生一个问题,v1 的值到底再定义时扩展 (静态扩展),还是在运行时扩展 (动态扩展)。如果 v2 的值是动态的,这两种扩展方式的结果可能会差异很大。

为了解决类似问题,Makefile 一共提供了四个赋值运算符 (=、:=、?=、+=)

VARIABLE = value
# 在执行时扩展,允许递归扩展。

VARIABLE := value
# 在定义时扩展。

VARIABLE ?= value
# 只有在该变量为空时才设置值。

VARIABLE += value
# 将值追加到变量的尾端。

内置变量

Make 提供一系列内置变量,比如 $(CC) 指向当前使用的编译器,$(MAKE) 指向当前使用的 Make 工具。这主要是为了跨平台的兼容性,详细的内置变量清单见 手册

output:
    $(CC) -o output input.c

自动变量

Make 命令还提供一些自动变量,它们的值与当前规则有关。主要有以下几个。

$@

$@ 指代当前目标,就是 Make 命令当前构建的那个目标。比如,make foo 的 $@ 就指代 foo。

a.txt b.txt:
    touch $@

等同于下面的写法

a.txt:
    touch a.txt
b.txt:
    touch b.txt

$<

$&lt; 指代第一个前置条件。比如,规则为 t: p1 p2 那么 $&lt; 就指代 p1。

a.txt: b.txt c.txt
    cp $< $@

等同于下面的写法

a.txt: b.txt c.txt
    cp b.txt a.txt

$?

$? 指代比目标更新的所有前置条件,之间以空格分隔。比如规则 t: p1 p2 其中 p2 的时间戳比 t 新,$? 就指代 p2。

$^

$^ 指代所有前置条件,之间以空格分隔。比如,规则 t: p1 p2 那么 $^ 就指代 p1 p2。

$*

$* 指代匹配符 % 匹配的部分,比如 % 匹配 f1.txt 中的 f1,$* 就表示 f1

$(@d) 和 $(@f)

$(@d) 和 $(@f) 分别指向 $@ 的目录名和文件名。比如,$@ 是 src/input.c 那么 $(@d) 的值为 src, $(@f) 的值为 input.c

$(&lt;D) 和 $(<F)

$(&lt;D) 和 $(<F) 分别指向 $< 的目录名和文件名。

下面是一个自动变量的例子

dest/%.txt: src/%.txt
    @[-d dest] || mkdir dest
    cp $< $@

上面代码将 src 目录下的 txt 文件,拷贝到 dest 目录下。首先判断 dest 目录是否存在,如果不存在就新建,然后 $&lt; 指代前置文件 src/%.txt,$@ 指代目标文件 dest/%.txt。

判断和循环

Makefile 使用 Bash 语法,完成判断和循环。

ifeq ($(CC), gcc)
    libs=$(libs_for_gcc)
else
    libs=$(normal_libs)
endif

上面的代码判断当前编译器是否是 gcc,然后指定不同的库文件。

LIST = one two three
all:
    for i in $(LIST); do \
        echo $$i \
    done

# 等同于
all:
    for i in one two three; do \
        echo $i; \
    done

上面代码运行结果

one
two
three

函数

Makefile 还可以使用函数,格式如下。

$(function arguments)

# 或者
${function arguments}

Makefile 提供了许多 内置函数,可供调用。下面是几个常用的内置函数。

shell

shell 函数用来执行 shell 命令

srcfile := $(shell echo src/{00..99}.txt)

wildcard

wildcard 函数用来在 Makefile 中,替换 Bash 的通配符。

srcfiles := $(wildcard src/*.txt)

subst

subst 函数用来文本替换,格式如下。

$(subst from, to, text)

下面的例子将字符串 "feet on the street" 替换成 "fEEt on the strEEt"。

$(subst ee, EE, feet on the street)

下面是一个稍微复杂的例子

comma :=,
empty :=

# space 变量用两个空变量作为表示符,当中一个是空格
space := $(empty) $(empty)
foo := a b c
bar := $(subst $(space),$(comma),$(foo))

patsubst

patsubst 函数用于模式匹配的替换,格式如下。

$(patsubst pattern, replacement, text)

下面的例子将文件名 "x.c.c bar.c" 替换成 "x.c.o bar.o"

$(patsubst %.c, %.o, x.c.c bar.c)

替换后缀名

替换后缀名的写法是: 变量名 + 冒号 + 后缀名替换规则。它实际上是 patsubst 函数的一种简写形式。

min: $(OUTPUT:.js=.min.js)

上面代码的意思是,将变量 OUTPUT 中的后缀名 .js 全部替换成 .min.js

Makefile实例

执行多个目标

.PHONY: cleanall cleanobj cleandiff

cleanall: cleanobj cleandiff
    rm program

cleanobj:
    rm *.o

cleandiff:
    rm *.diff

编译 c 语言项目

edit: main.o kbd.o command.o display.o
    cc -o $@ $^

main.o: main.c defs.h
    cc -c main.c

kdb.o: kdb.c defs command.h
    cc -c command.c

command.o: command.c defs.h command.h
    cc -c command.c

display.o: display.c defs.h
    cc -c display.c

clean:
    rm edit main.o kdb.o command.o display.o

.PHONY: edit clean

C++实现字符串类

作业

接口声明

//
// Created by aydon on 2021/5/13.
//

#ifndef STRING_STRING_H
#define STRING_STRING_H

#include <cstring>
#include <iostream>

class String {
private:
    char *data_;
    size_t size_;

public:
    String();

    String(const char *str);                 // 构造
    String(const String &other);             // 拷贝构造
    ~String();                               // 析构
    String &operator=(const String &other);  // 拷贝赋值

    String(String &&other) noexcept;             // 移动构造
    String &operator=(String &&other) noexcept;  // 移动赋值
    char &operator[](size_t index);                    // 重载[]操作符
    bool operator==(const String &other);              // 重载==操作符
    String operator+(const String &other);

    const char *c_str() const;

    size_t length();

    friend std::ostream &operator<<(std::ostream &out, const String &str);

    friend std::istream &operator>>(std::istream &in, String &str);

private:
    void __init(const char *str); // 分配内存
    void __clean();                           // 清除内存
    void __swap(String &other); // 交换 用于移动构造
};
#endif //STRING_STRING_H

实现

//
// Created by aydon on 2021/5/13.
//

#include "String.h"

String::String() : data_(nullptr), size_(0) {
    // 默认构造函数
    // 初始化 data_ 为 nullptr, size_ 为 0
}

String::String(const char *str) {
    __init(str);
}

String::String(const String &other) {
    std::cout << "copy ct" << std::endl;
    __init(other.data_);
}

String::~String() {
    __clean();
}

String &String::operator=(const String &other) {
    // 拷贝赋值前需要先将原先声明的空间释放,防止内存产生泄漏
    std::cout << "copy as" << std::endl;
    if (this != &other) {
        __clean();
        __init(other.data_);
    }
    return *this;
}

// c++11 引入了 noexcept 运算符,用于指定某个函数不抛出异常
// 加入 noexcept 后,编译器会做特殊优化处理
// 如果函数声明时加入了 noexcept 那么函数定义时也需要加入 noexcept
String::String(String &&other) noexcept {
    std::cout << "move ct" << std::endl;
    __clean();
    __swap(other);
}

String &String::operator=(String &&other) noexcept {
    if (this != &other) {
        __clean();
        __swap(other);
    }
    return *this;
}

char &String::operator[](size_t index) {
    if (index < 0 || index >= size_) {
        throw std::out_of_range("Index out of index");
    }
    return data_[index];
}

bool String::operator==(const String &other) {
    if (size_ != other.size_) {
        return false;
    }
    return strcmp(data_, other.data_) == 0;
}

String String::operator+(const String &other) {
    String s;
    s.size_ = size_ + other.size_;
    s.data_ = new char[s.size_ + 1];
#ifdef _MSC_VER
    strncpy_s(s.data_, size_ + 1, data_, size_);
    strncpy_s(s.data_ + size_, other.size_ + 1, other.data_, other.size_);
#else
    strncpy(s.data_, data_, size_);
    strncpy(s.data_ + size_, other.data_, other.size_);
#endif
    return s;
}

const char *String::c_str() const {
    return data_;
}

size_t String::length() {
    return size_;
}

void String::__init(const char *str) {
    int len = strlen(str);
    if (len == 0) {
        data_ = nullptr;
        size_ = 0;
    } else {
        data_ = new char[len + 1];
        size_ = len;
#ifdef _MSC_VER
        strcpy_s(data_, len + 1, str);
#else
        strcpy(data_, str);
#endif
    }
}

void String::__clean() {
    if (!size_ && data_ != nullptr) {
        delete[] data_;
    }
    size_ = 0;
}

void String::__swap(String &other) {
    size_t temp_size = other.size_;
    char *temp_data = other.data_;
    other.data_ = data_;
    other.size_ = size_;
    data_ = temp_data;
    size_ = temp_size;
}

std::ostream &operator<<(std::ostream &out, const String &str) {
    if (!str.data_) {
        out << "";
    } else {
        out << str.data_;
    }
    return out;
}

std::istream &operator>>(std::istream &in, String &str) {
    std::cout << "Please input string size: ";
    in >> str.size_;
    str.data_ = new char[str.size_ + 1];
    std::cout << "Please input string data: ";
    in >> str.data_;
    if (strlen(str.data_) > str.size_) {
        std::cout << "error! data len > size" << std::endl;
        exit(1);
    }
    return in;
}

CMake 文件

cmake_minimum_required(VERSION 3.10)
project(string)

set(CMAKE_CXX_STANDARD 11)

aux_source_directory(src STR_LISTS)

add_executable(main test.cpp ${STR_LISTS})

输出结果

D:\Gallery\LearningProjects\string\cmake-build-debug\main.exe
copy constructor test
copy ct
copy assignment test
copy as
move assignment test
jack
0
fluy
move construct test
hello
operator<< test
hello
operator>> test
Please input string size:2
Please input string data:hi
hi
operator== test
1,0
operator+ test
lucy
c_str() test
lucy
length() test
4

Process finished with exit code 0

笔记

noexcept 运算符

c++11 引入了 noexcept 运算符,用于指定某个函数不抛出异常。加入 noexcept 后,编译器会做特殊优化处理。如果函数声明时加入了 noexcept 那么函数定义时也需要加入 noexcept

编译器判断

  • MSVC: _MSC_VER
  • GCC: __GNUC__

strcpy_s 函数

/**
 * @brief 字符串拷贝函数
 * @param _Destination 目标字符串指针
 * @param _SizeInBytes 字符串长度[可以使用 "strlen() + 1" 取得]
 * @param _Source      输入字符串
 */
_ACRTIMP errno_t __cdecl strcpy_s(
_Out_writes_z_(_SizeInBytes) char*       _Destination,
_In_                         rsize_t     _SizeInBytes,
_In_z_ char const* _Source
);

成员函数调用同类型实例对象的私有变量

在使用时发现如下代码中,可以在函数中直接调用对象的私有变量。在印象中 private 修饰的变量应该是不对外公开的,应该不能在除了类中的其他地方访问😓。查阅资料了解到封装是编译期的概念,是针对类而非对象,在类的成员函数中可以访问同类型实例对象的私有成员变量。

class String {
private:
    char *data_;
    size_t size_;
    ...
    ...
};

String String::operator+(const String &other) {
    String s;
    s.size_ = size_ + other.size_;
    s.data_ = new char[s.size_ + 1];
    strncpy_s(s.data_, size_ + 1, data_, size_);
    strncpy_s(s.data_ + size_, other.size_ + 1, other.data_, other.size_);
    return s;
}

拷贝/移动赋值检查

在使用拷贝/移动赋值时,需要进行检查,以防在执行 __clean() 时发生错误。

String &String::operator=(const String &other) {
    // 拷贝赋值前需要先将原先声明的空间释放,防止内存产生泄漏
    std::cout << "copy as" << std::endl;
    if (this != &other) {
        __clean();
        __init(other.data_);
    }
    return *this;
}

输入时加入判断

在使用 istream 输入内容时,需要对输入的数据进行判断,如果输入的数据长度超过指定的长度应退出程序

std::istream &operator>>(std::istream &in, String &str) {
    std::cout << "Please input string size: ";
    in >> str.size_;
    str.data_ = new char[str.size_ + 1];
    std::cout << "Please input string data: ";
    in >> str.data_;
    
    // 这里进行判断
    if (strlen(str.data_) > str.size_) {
        std::cout << "error! data len > size" << std::endl;
        exit(1);
    }
    return in;
}

出现 double free 的情况

在使用 gnu-linux 编译项目执行测试时出现了 double free 的情况如下所示。

free(): double free detected in tcache 2

修改 __clean 函数判断当前数据是否为空

// 错误情况
void String::__clean() {
    delete[] data_;
    size_ = 0;
}

// 正确
void String::__clean() {
    if (!size_ && data_ != nullptr) {
        delete[] data_;
    }
    size_ = 0;
}

使用 MSVC 时报内存错误

使用 MSVC 编译项目时执行移动构造函数时 CLion 显示如下内容

Process finished with exit code -1073741819 (0xC0000005)

发现时在执行 _swap 函数时没有判断 data 是否为 nullptr 修改内容为下方所示

void String::__swap(String &other) {
    size_t temp_size = other.size_;
    char *temp_data = other.data_;
    
    if (data_ == nullptr || size_ == 0) {
        other.data_ = nullptr;
        size_ = 0;
    } else {
        other.data_ = data_;
        other.size_ = size_;
    }
    data_ = temp_data;
    size_ = temp_size;
}

python字符串前u、r、b、f含义

字符串前加 u

s = u'你好啊~'

字符串前加 u 代表该字符串使用 unicode 编码,改修饰符仅在 python2 中使用,因为在 python3 中所有字符串都为 unicode 编码。

字符串前加 r

path = r'D:\windows\helloworld.py'

字符串前加 r 代表不转义转义字符,字符串按原本格式输出,忽视转义字符。

字符串前加 b

data = b'\x00\x32'

字符串前加 b 代表该字符串为二进制数据,python 会将字符串自动转换成二进制形式

字符串前加 f

text = 'Hello World'
message = f'{text}!'

# 输出
# Hello World!

字符串前加 f 代表 format,可以在字符串中使用变量,使用变量用大括号包裹即可

信噪比

信噪比指的是音频信号与噪音之间的比值,信噪比越大音频质量越好.

信噪比 = (Signal / Noise) 

使用 github issue 搭建博客

用过 csdn 编写博客,也用过 github pages 搭建个人博客。github issue 使用起来感觉更加随意一些,可以记录一些随笔等内容,可以自定义标签,以及 project 来管理文章。使用起来感觉更好。

nginx+grpc配置负载均衡

nginx 1.14 版本后已经支持了 grpc 的负载均衡,只需使用 grpc_pass 即可对 grpc 服务进行负载均衡。

user nginx;
worker_processes 1;

error_log       /var/log/nginx/error.log warn;
pid             /var/run/nginx.pid;


events {
    use epoll;
    worker_connections 1024;
}


http {
    upstream correct {
        least_conn;
        server 172.17.0.1:9003;
        server 172.17.0.1:9004;
    }

    server {
        listen 80 http2;

        location / {
            grpc_pass grpc://correct;
        }
    }
}

axios设置跨域

axios的跨域配置。基于vue-cli3.0以上版本

安装 axios

vue add axios

安装axios后,在src目录下会生成一个plugins文件夹,里面有axios.js文件。

配置跨域设置

在项目根目录下创建vue.config.js文件,并按照如下内容配置

module.exports = {
    devServer: {
        proxy: {
            '/api': {
                target: 'http://127.0.0.1:12700',
                changeOrigin: true,
                pathRewrite: {
                    '^/api': ''
                }
            }
        }
    }
}

接下来在请求时加入/api的请求都是代理到http://127.0.0.1:12700路径上。

例如:

 this.axios.get('/api/sso_get_account')
          .then(res => {
            console.log(res)
            that.message = eval(res.data[0])
          })
          .catch(err => {
            console.log(err)
          })

python yield 笔记

yield 的作用是返回一个迭代器。如果函数中含有 yield 那么使用普通函数调用的方式时该函数不会真正执行,而是会返回一个迭代器。

测试代码

def foo():
    print("yield func starting")
    while True:
        res = yield 4
        print(f"res: {res}")


if __name__ == '__main__':
    func_res = foo()
    print(func_res)

输出

# 可以看到当我们打印函数输出时显示的是一个迭代器
<generator object foo at 0x000001F126322EB0>

由此来看 yield 其实是将一个函数变成了一个对象,我们在调用函数时其实是声明了一个对象。

获取含有 yield 函数的输出

我们无法通过调用一般函数调用的方式获取含有 yield 函数的返回值,此时我们需要一个内置函数 next 来帮助我们得到函数返回值。程序与输出如下所示。

测试代码

def foo():
    print("yield func starting")
    while True:
        res = yield 4
        print(f"res: {res}")


if __name__ == '__main__':
    func_res = foo()
    print(next(func_res))
    print("-" * 20)
    print(next(func_res))

输出

yield func starting
4
--------------------
res: None
4

上述程序的执行顺序如下所示

  1. 执行函数到 yield 所在行
  2. 返回等号右侧 yield 表达式的值
  3. 再次执行函数从 yield 所在行的左侧执行
  4. 重新执行 1、2 步

除使用 next 函数调用函数,我们也可以通过使用 迭代器对象.send(value) 的方式调用 yield 函数。(注:如若使用 send 函数需在此之前执行过至少一次 next 函数)send 函数的作用是将输入的值赋值给 yield 等号左侧的变量。将上方程序修改为如下所示。

测试代码

def foo():
    print("yield func starting")
    while True:
        res = yield 4
        print(f"res: {res}")


if __name__ == '__main__':
    func_res = foo()
    print(next(func_res))
    print("-" * 20)
    print(func_res.send(2))

输出

yield func starting
4
--------------------
res: 2
4

面向对象的形式说明 yield 函数

既然含有 yield 的函数的返回值是一个迭代器对象,那么我们可以用面向对象的方式来介绍 yield 函数。

程序

class foo:
    def __init__(self):
        self.res = None
   
    def set_res(self, res):
        self.res = res

    def get_res(self):
        return self.res

    def run(self):
        return 4


if __name__ == '__main__':
    f = foo()
    print(f.run())
    print("-" * 20)
    f.set_res(2)
    print(f.get_res())

输出

yield func starting
4
--------------------
res: 2
4

上述的 yield 程序可以大致修改为上方的类描述。

修改 hosts 文件提升 github 访问速度

链接测试 这个网页中输入 github.com 查看延迟最低的 ip 地址。

修改 hosts 文件

# windows 环境下
C:\Windows\System32\drivers\etc\hosts

# linux 环境下
sudo vim /etc/hosts

修改 hosts 文件在最后一行加入之前搜索到的延迟最低的 IP 地址,输入内容如下所示

13.229.188.59    github.com

C++ inline 关键字

inline 表示的是内联的意思。使用 inline 修饰的函数称为内联函数。

内联函数的用途

使用内联函数在程序编译阶段,编译器会对内联函数做优化。编译器会将内联函数的函数体内容替换内联函数的调用处,使得程序减少函数调用的开销,从而加速程序。函数是否能够成功内联取决于编译器,并不是函数使用 inline 关键字就能成功内联。

// 未编译程序
inline void display() {
    printf("Hello World");
}

int main() {
    display();
}

// 编译后程序
int main() {
    printf("Hello World");
}

不适用内联函数的情况

  1. 内联函数中的函数体长度较长
  2. 内联函数中有循环体

类中内联

在类中使用内联时需要注意,如果该函数为虚函数且该函数实现了多态性,那么编译器不会对这种函数进行内联操作。

class Demo {
public:
    Demo() {
        printf("Demo constructor\n");
    }

    inline virtual void display() {
        printf("I'm Demo\n");
    }

    virtual ~Demo() = default;
};

class DemoKid : public Demo {
public:
    DemoKid() {
        printf("DemoKid constructor\n");
    }

    inline void display() override {
        printf("I'm DemoKid\n");
    }
};

int main() {
    Demo demo;
    demo.display();

    Demo *demo_ptr = new DemoKid();
    // 触发多态性,该函数不会内联
    demo_ptr->display();
    return 0;
}

输出

Demo constructor
I'm Demo
Demo constructor
DemoKid constructor
I'm DemoKid

注意事项

内联函数应在函数定义处使用,不要再函数声明时加入。

// inline-test.h
void Foo();

// inline-test.cc
// 在函数定义时加入 inline 关键字
inline void Foo() {
     printf("Hello World");
}

CMake 使用 Boost

Boost 很多的库都是 Header Only 的库,在官网的文档中有说明这一条内容。
image
同时官网也列举了哪些库是需要编译后使用的。
image

如何使用

  1. 在 CMakeLists.txt 文件中指定 BOOST_ROOT 的值(如果将 Boost 安装到 /usr/local 下则无需指定)
  2. 使用 find_package(Boost) 找到 Boost
  3. 为项目添加 Boost 头文件依赖

CMakeLists.txt

project(boost-test)

set(BOOST_ROOT "/path/to/your/boost_1_75_0")

find_package(Boost REQUIRED)

if(Boost_FOUND)
    add_executable(${PROJECT_NAME} main.cpp)
    target_link_libraries(${PROJECT_NAME} pthread)
    target_include_directories(${PROJECT_NAME} PRIVATE ${BOOST_ROOT}/)
endif()

测试代码

#include <boost/lambda/lambda.hpp>
#include <iostream>
#include <iterator>
#include <algorithm>

int main()
{
    using namespace boost::lambda;
    typedef std::istream_iterator<int> in;

    std::for_each(
        in(std::cin), in(), std::cout << (_1 * 3) << " " );
}

输出结果

# 输入
3 6 9

# 输出
369

rapidjson 解析 json 文件

rapidjson 官方文档

rapidjson 安装

rapidjson 是一个 header-only 的库,只需把 rapidjson 项目中的 include 文件夹复制到项目中即可使用。

rapidjson 解析 json 文件

例子如下

#include "rapidjson/document.h"
#include "rapidjson/istreamwrapper.h"

using namespace std;
int main() {
    string file_path = "path/to/your/file.json";
    ifstream file(file_path);
    IStreamWrapper json(file);
    Document d;
    d.ParseStream(json);
    
    cerr << d["test"].GetString() << "\n";
    return EXIT_SUCCESS;
}

json文件

{
    "text": "hello world",
}

输出

hello world

输出

git统计代码行数

全部统计

git log --author="$(git config --get user.name)" --pretty=tformat: --numstat | awk '{ add += $1 ; subs += $2 ; loc += $1 - $2 } END { printf "added lines: %s removed lines : %s total lines: %s\n",add,subs,loc }' -

输出

added lines: 31272 removed lines : 2680 total lines: 28592

统计部分(排除某些文件夹)

git log --author="$(git config --get user.name)" --pretty=tformat: --numstat -- . ":(exclude)src" | awk '{ add += $1 ; subs += $2 ; loc += $1 - $2 } END { printf "added lines: %s removed lines : %s total lines: %s\n",add,subs,loc }' -

输出

added lines: 7225 removed lines : 160 total lines: 7065

python websockets库的使用

使用一个 echo 服务举例

服务端

import asyncio
import websockets


class Demo:
    def __init__(self):
        self.count = 0


async def echo(websocket, path):
    demo = Demo()
    while True:
        message = await websocket.recv()
        print(demo.count)
        demo.count += 1
        await websocket.send(message)


start_server = websockets.serve(echo, "localhost", 8765)

asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()

客户端

import asyncio
import websockets


async def hello():
    uri = "ws://localhost:8765"
    async with websockets.connect(uri) as websocket:
        while True:
            message = input("put your message: ")
            await websocket.send(message)
            response = await websocket.recv()
            print(response)


asyncio.get_event_loop().run_until_complete(hello())

使用 bcp 裁剪 boost 库

使用 boost 库时仅使用了 boost 库中的部分内容,如果把 boost 库全部放入到项目当中会使项目变得十分庞大。boost 官方提供了一个 bcp 工具来帮助我们裁剪 boost 库。这里的操作都在 linux 下执行,如果需要在 windows 平台下执行可以根据 linux 的执行方式进行修改。

下载 boost

# 下载 boost 源文件
wget https://dl.bintray.com/boostorg/release/1.75.0/source/boost_1_75_0.tar.gz

# 解压
tar -zxvf boost_1_75_0.tar.gz

安装 bcp 裁剪工具

以下操作都在 boost 的根目录下执行。

# 运行 bootstrap.sh 脚本安装 b2
./bootstrap.sh

## 安装 bcp 工具
./b2 tools/bcp

安装好后的 bcp 工具在当前目录下的 dist/bin 目录下。

使用 bcp 裁剪 boost

bcp 工具的使用方式为 bcp <要使用的boost内容> <输出文件夹>,例子如下所示。

# 裁剪 beast 项目
./dist/bin/bcp beast output

裁剪后的内容在 output 目录下的 boost 文件夹中。
[注意]:输出的文件夹必须要存在

原始 boost 文件内容

image

裁剪后 boost 文件夹内容

image

C++ this 指针

每个类中的非静态成员函数都会有一个隐藏的参数 this,该参数指向的是每个类的实例。例如有类有如下定义

class Demo {
public:
    Demo(int num) {
        m_num = num;
    }

    int get_num() const {
        return m_num;
    }

   void add_num(int num) {
        m_num += num;
   }
private:
    int m_num;
};

在调用类非静态成员函数时,非静态函数的实际定义为

void (Demo * const this, int num) {
    m_num += num;
}

git提交emoji表情

🎨 (调色板) 🎨 改进代码结构/代码格式
⚡️ (闪电) ⚡️ 提升性能
🐎 (赛马) 🐎 提升性能
🔥 (火焰) 🔥 移除代码或文件
🐛 (bug) 🐛 修复 bug
🚑 (急救车) 🚑 重要补丁
✨ (火花) ✨ 引入新功能
📝 (铅笔) 📝 撰写文档
🚀 (火箭) 🚀 部署功能
💄 (口红) 💄 更新 UI 和样式文件
🎉 (庆祝) 🎉 初次提交
✅ (白色复选框) ✅ 增加测试
🔒 (锁) 🔒 修复安全问题
🍎 (苹果) 🍎 修复 macOS 下的问题
🐧 (企鹅) 🐧 修复 Linux 下的问题
🏁 (旗帜) 🏁 修复 Windows 下的问题
🔖 (书签) 🔖 发行/版本标签
🚨 (警车灯) 🚨 移除 linter 警告
🚧 (施工) 🚧 工作进行中
💚 (绿心) 💚 修复 CI 构建问题
⬇️ (下降箭头) ⬇️ 降级依赖
⬆️ (上升箭头) ⬆️ 升级依赖
👷 (工人) 👷 添加 CI 构建系统
📈 (上升趋势图) 📈 添加分析或跟踪代码
🔨 (锤子) 🔨 重大重构
➖ (减号) ➖ 减少一个依赖
🐳 (鲸鱼) 🐳 相关工作
➕ (加号) ➕ 增加一个依赖
🔧 (扳手) 🔧 修改配置文件
🌐 (地球) 🌐 国际化与本地化
✏️ (铅笔) ✏️ 修复 typo

数据库三级封锁协议

数据库三级封锁协议是为了对数据库并发操作进行控制,防止更改丢失,读脏数据,不可重复读等问题.为了解决这类问题引入的解决方案就是加锁,又根据添加锁的种类,设置时间,释放时间的不同分为三种封锁协议.

锁的种类

在数据库的三级封锁协议中分为两种锁

  • 排他锁(exclusive lock,X锁),又称为写锁
  • 共享锁(shared lock,S锁),又称为读锁

排他锁(X锁)

排他锁,又称为写锁,可以对数据进行读取和修改操作.当一个事务对数据添加X锁时,其他事务都不能对该数据添加锁,即同一时间只有一条事务可以对数据进行修改操作.其他事务只能等待该事务完成后才能对数据进行修改操作.所以我们通常把这类对数据进行修改操作的锁称为写锁.

共享锁(S锁)

共享锁,也可以称为读锁.当一个事务对数据添加S锁时,其他事务也可以对数据添加S锁,即所有添加了S锁的事务都可以同时读取数据内容,但是无法对数据进行修改.

更改丢失

更改丢失指的是当有多个事务对数据进行操作时,有些事务的操作被其他事务所替代.

image

从上图中我们可以看出 T1 事务的修改操作被 T2 事务的修改操作覆盖了.

不可重复读

不可重复读指的是某一个事务执行期前其他的事务修改,插入或删除了数据,则该事务在验证数据时会出现与开始结果不一致的情况.

image

读脏数据

读脏数据指的是某一个事务读取了另一个事务执行期间的数据内容,当另一个事务回滚时,该事务读取到的内容就是无效数据.

image

从上图中可以看到 T2 事务读取到的 C 的值是一个无效的数据.

三类封锁协议

第一类封锁协议

第一类封锁协议指的是在对数据进行修改操作时需要对数据添加X锁.第一类封锁协议相当于把数据的读取和修改看成一个整体,在事务完成之前其他事务都不能对数据进行修改操作.因此第一类封锁协议解决了丢失修改的问题.另外要注意如果其他事务仅是对数据进行读取操作时无需加锁,因此其他事务在执行过程中还是可能会出现不可重复读和读脏数据的情况.

image

上图显示了第一类封锁协议解决丢失修改的问题.

image

上图显示了第一类封锁协议出现的读脏数据情况.

image

上图显示了第一类封锁协议出现的不可重复读情况.

第二类封锁协议

第二类封锁协议是在第一类封锁协议的基础上加入了S锁.在读取数据前需要对数据添加S锁, 当数据读取完成后释放S锁 .如果一个事务读取数据并添加了S锁,另一个事务添加了X锁,那么添加X锁的那个事务必须等待添加了S锁的事务释放S锁后才能对数据进行修改操作.基于这个缘由第二类封锁协议解决了读脏数据的问题.但是第二类封锁协议并不能解决数据的不可重复读问题.如下图所示.

image

第三类封锁协议

第三类封锁协议是在第一类封锁协议的基础上加入了S锁,在读取数据前需要对数据添加S锁, 当事务结束后释放S锁.第三类封锁协议同时解决了数据的修改丢失,不可重复读和读脏数据问题.

可重复读
image

不读脏数据
image

总结

协议名 解决问题 不可解决的问题
第一类封锁协议 丢失修改 读脏数据,不可重复读
第二类封锁协议 丢失修改,读脏数据 不可重复读
第三类封锁协议 丢失修改,读脏数据,不可重复读

声明

本文章的所有图片都来自于 二级封锁协议-**科学技术大学 的 PPT 中,如有侵权立即删除.

centos7+ docker 因防火墙无法通信

使用 docker 时要与其他程序进行网络通信,bridge 模式下 docker 受防火墙干扰无法通信。

关闭防火墙

centos7+ 之后防火墙使用 firewalld 替代了之前的 iptables。

# 关闭防火墙
systemctl stop firewalld

出现的问题

关闭防火墙后仍无法进行通信,报错信息中含有内容 docker0: iptables: No chain/target/match by that name. 出现此内容是因为关闭 firewalld 后 iptables 被激活,此时的 docker chain 尚未被加入到 iptables 中。需要重启 docker 将 docker chain 加入。

# 重启 docker
service docker restart 

执行 iptables -L 查看 docker0 是否被加入 iptable 中。此时可以看到 Chain DOCKER 证明成功。
image

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.