C++11 智能指針 shared_ptr

来源:https://www.cnblogs.com/champrin/archive/2023/01/17/17056814.html
-Advertisement-
Play Games

C++11 智能指針 shared_ptr Written on 2023-01-16 個人學習智能指針記錄合集: C++11 智能指針 C++11 智能指針 shared_ptr C++11 智能指針 unique_ptr C++11 智能指針 weak_ptr std::shared_ptr 共 ...


C++11 智能指針 shared_ptr

Written on 2023-01-16

個人學習智能指針記錄合集:

std::shared_ptr 共用智能指針,也被稱為計數智能指針。

共用智能指針會記錄有多少個共用智能指針指向同一個對象,當這個數為 0 的時候,程式自動的預設釋放(析構)這個對象,記錄有多少個的這個方法叫做引用計數

共用智能指針可以有多個共用智能指針同時管理同一個對象。

舉個慄子

普通指針管理

#include <iostream>
#include <memory>
using namespace std;

class Person{
public: 
    Person(){ cout << "Constructor: person's age = " << m_age << endl; }
    Person(int age) : m_age(age){ cout << "Constructor: person's age = " << m_age << endl; }
    ~Person(){ cout << "Destructor: person's age = " << m_age << endl; }
    void getAge(){ cout << "Person's age = " << m_age << endl; }
private:
    int m_age = 0; 
}; 

int main()
{
    {
        Person *p = new Person(18);
    }

    cout << endl << "Before return" << endl;
    return 0;
}
/** 輸出:
Constructor: person's age = 18

Before return

**/

依輸出可見,p指向的對象並沒有被析構,因為並沒有析構函數中的列印,這造成了記憶體泄漏。

shared_ptr智能指針管理

// ...
int main()
{
    {
        shared_ptr<Person> sPtr1 {new Person(18)};
    }

    cout << endl << "Before return" << endl;
    return 0;
}
/** 輸出:
Constructor: person's age = 18
Destructor: person's age = 18

Before return

**/

依輸出可見,離開了程式塊的作用域後,析構函數中的列印體現出來了,sPtr1管理的對象自動的被析構了。

獲取引用計數

long use_count() const noexcept;

shared_ptrnullptr 時,返回為 0。

初始化

創建空管理的 shared_ptr

constexpr shared_ptr() noexcept;
constexpr shared_ptr(std::nullptr_t) noexcept;
  • std::nullptr_t:空指針nullptr

創建對非數組對象和數組對象管理的 shared_ptr

template< class Y >
explicit shared_ptr( Y* ptr );
  • Y:動態分配的對象的類型
// ...
int main()
{
    shared_ptr<int> sPtr1; // 創建空管理的shared_ptr
    cout << "sPtr1 use count = " << sPtr1.use_count() << endl;
    
    shared_ptr<int> sPtr2(nullptr); // 創建空管理的shared_ptr
    cout << "sPtr2 use count = " << sPtr2.use_count() << endl;
    
    shared_ptr<int> sPtr3(new int(100)); // 創建對非數組對象管理的shared_ptr
    cout << "sPtr3 use count = " << sPtr3.use_count() << endl;
    
    shared_ptr<int> sPtr4(new int[10]); // 創建對數組對象管理的shared_ptr
    cout << "sPtr4 use count = " << sPtr4.use_count();
    return 0;
}
/** 輸出:
sPtr1 use count = 0
sPtr2 use count = 0
sPtr3 use count = 1
sPtr4 use count = 1
**/

解釋:
sPtr1sPtr2都為nullptr,故引用計數都為 0;
sPtr3sPtr4所管理的對象,都只有一個 shared_ptr 管理,故引用計數都為 1。

創建指定刪除器的 shared_ptr

template< class Y, class Deleter >
shared_ptr( Y* ptr, Deleter d );

template< class Deleter >
shared_ptr( std::nullptr_t ptr, Deleter d );

template< class Y, class Deleter, class Alloc >
shared_ptr( Y* ptr, Deleter d, Alloc alloc );

template< class Deleter, class Alloc >
shared_ptr( std::nullptr_t ptr, Deleter d, Alloc alloc );
  • Deleter:刪除器,在引用計數為 0 時,shared_ptr 會自動的執行刪除器
  • Alloc:分配器(Allocator)
  1. 預設刪除器:預設對指向的記憶體地址進行釋放,即析構掉這塊地址的內容
    1. 非數組類型,以 delete ptr 為預設刪除器
    2. 數組類型,以 delete[] ptr 為預設刪除器
  2. 指定刪除器:能夠自行指定引用計數為 0 時,要做什麼,比如有些場景不需要對指向的記憶體進行釋放,比如關閉指向文件,關閉指向套接字
    1. 指定刪除器的函數結構:
      void deletePtr(T* p){ ... }
      
    2. 也可使用 Lambda表達式

例,對於文件的使用場景,不是直接delete文件,而是關閉文件:

// ...
void closeFile(FILE* fp) 
{
    if (fp == nullptr) return;
    fclose(fp);
    cout << "File closed" << endl;
}
int main() 
{
    FILE* fp = fopen("data.txt" ,"w");
    shared_ptr<FILE> sfp{fp,closeFile};
    if (sfp == nullptr)
        cout << "Failed opened" << endl;
    else 
        cout << "File opened" << endl;
    return 0;
}

創建通過現有共用智能指針的 shared_ptr

主要是通過複製構造函數和移動構造函數std::move()

shared_ptr( const shared_ptr& r ) noexcept;

template< class Y >
shared_ptr( const shared_ptr<Y>& r ) noexcept;

shared_ptr( shared_ptr&& r ) noexcept;

template< class Y >
shared_ptr( shared_ptr<Y>&& r ) noexcept;    

例,

// ...
int main()
{
    shared_ptr<int> sPtr1(new int(100));
    cout << "sPtr1 use count = " << sPtr1.use_count() << endl << endl;
    // 創建通過現有現有共用智能指針的 shared_ptr
    // 通過拷貝構造函數創建
    shared_ptr<int> sPtr2(sPtr1);
    cout << "sPtr1 use count = " << sPtr1.use_count() << endl;
    cout << "sPtr2 use count = " << sPtr2.use_count() << endl << endl;
    // 通過賦值運算符創建
    shared_ptr<int> sPtr3 = sPtr1;
    cout << "sPtr1 use count = " << sPtr1.use_count() << endl;
    cout << "sPtr2 use count = " << sPtr2.use_count() << endl;
    cout << "sPtr3 use count = " << sPtr3.use_count() << endl << endl;
    // 通過移動構造函數創建
    shared_ptr<int> sPtr4(move(sPtr1));
    cout << "sPtr1 use count = " << sPtr1.use_count() << endl;
    cout << "sPtr2 use count = " << sPtr2.use_count() << endl;
    cout << "sPtr3 use count = " << sPtr3.use_count() << endl;
    cout << "sPtr4 use count = " << sPtr4.use_count() << endl << endl;
    std::shared_ptr<int> sPtr5 = move(sPtr2);
    cout << "sPtr1 use count = " << sPtr1.use_count() << endl;
    cout << "sPtr2 use count = " << sPtr2.use_count() << endl;
    cout << "sPtr3 use count = " << sPtr3.use_count() << endl;
    cout << "sPtr4 use count = " << sPtr4.use_count() << endl;
    cout << "sPtr5 use count = " << sPtr5.use_count();
    return 0;
}
/** 輸出:
sPtr1 use count = 1

sPtr1 use count = 2
sPtr2 use count = 2

sPtr1 use count = 3
sPtr2 use count = 3
sPtr3 use count = 3

sPtr1 use count = 0
sPtr2 use count = 3
sPtr3 use count = 3
sPtr4 use count = 3

sPtr1 use count = 0
sPtr2 use count = 0
sPtr3 use count = 3
sPtr4 use count = 3
sPtr5 use count = 3
**/

解釋:
sPtr2sPtr3都是通過對sPtr1執行了複製構造函數,因此sPtr1的引用計數一次增加 1;sPtr2sPtr3同理。
通過move(sPtr1),使得sPtr1釋放了被管理對象的所有權,此時sPtr1被設置為nullptr,因此sPtr1引用計數為 0;sPtr2同理。

通過 std::make_shared

template< class T, class... Args >
shared_ptr<T> make_shared( Args&&... args );
  • T:非數組,指針指向的數據類型
  • Args&&... argsT 的構造函數參數列表

這種初始化效率更高,在 C++17 之前的編譯器更安全

// ...
int main()
{
    shared_ptr<int> sPtr1 = make_shared<int>(100);
    shared_ptr<int> sPtr2(make_shared<int>(200));
    shared_ptr<int> sPtr3{make_shared<int>(300)};
    return 0;
}

通過 .reset( ptr )

使用.reset( ptr ),使得釋放對shared_ptr原管理對象的所有權,轉為對新對象管理的所有權。

void reset() noexcept;

template< class Y >
void reset( Y* ptr );

template< class Y, class Deleter >
void reset( Y* ptr, Deleter d );

template< class Y, class Deleter, class Alloc >
void reset( Y* ptr, Deleter d, Alloc alloc );

註意.reset( ptr )若傳入的所指向的對象已被占有,程式會異常。

// ...
int main()
{
    shared_ptr<int> sPtr1(new int(100));
    shared_ptr<int> sPtr2 = sPtr1;
    shared_ptr<int> sPtr3 = sPtr2;
    shared_ptr<int> sPtr4 = sPtr1;
    cout << "sPtr1 use count = " << sPtr1.use_count() << endl;
    cout << "sPtr2 use count = " << sPtr2.use_count() << endl;
    cout << "sPtr3 use count = " << sPtr3.use_count() << endl;
    cout << "sPtr4 use count = " << sPtr4.use_count() << endl << endl;
    
    sPtr4.reset();
    cout << "sPtr1 use count = " << sPtr1.use_count() << endl;
    cout << "sPtr2 use count = " << sPtr2.use_count() << endl;
    cout << "sPtr3 use count = " << sPtr3.use_count() << endl;
    cout << "sPtr4 use count = " << sPtr4.use_count() << endl << endl;
    
    sPtr3.reset(new int(100));
    cout << "sPtr1 use count = " << sPtr1.use_count() << endl;
    cout << "sPtr2 use count = " << sPtr2.use_count() << endl;
    cout << "sPtr3 use count = " << sPtr3.use_count() << endl;
    cout << "sPtr4 use count = " << sPtr4.use_count();
    
    std::shared_ptr<int> sPtr5;
    sPtr5.reset(sPtr1.get()); // 異常
    return 0;
}
/** 輸出:
sPtr1 use count = 4
sPtr2 use count = 4
sPtr3 use count = 4
sPtr4 use count = 4

sPtr1 use count = 3
sPtr2 use count = 3
sPtr3 use count = 3
sPtr4 use count = 0

sPtr1 use count = 2
sPtr2 use count = 2
sPtr3 use count = 1
sPtr4 use count = 0
**/

解釋:

  1. 通過複製構造函數後,sPtr1-4的引用計數均為 4。
  2. sPtr4使用了reset()後,釋放被管理對象的所有權,被管理對象的shared_ptr個數減 1,sPtr4被設置為nullptr,同時,sPtr1-3的引用計數均變為 3。
  3. sPtr3使用了reset(new int(100))後,釋放原被管理對象的所有權,被管理對象的shared_ptr個數減 1,sPtr4被設置為新的管理對象int(100),同時,sPtr1-3的引用計數均變為 2。
  4. sPtr5reset傳入的所指向的對象已被占有,程式異常,沒有正常結束。

.reset() 釋放被管理對象的所有權

用於取消shared_ptr對管理對象的所有權;
當這個對象被shared_ptr管理的數量為 0,會執行刪除器。

使用.reset(),會使得shared_ptr設置為nullptr

獲取原始儲存的指針

T* get() const noexcept;

解引用存儲的指針 operator* and operator->

可以像普通指針一樣,使用shared_ptr對所管理的對象進行訪問。

// ...
int main()
{
    shared_ptr<int> sPtr1 {new int(100)};
    cout << *sPtr1 << endl << endl;
    
    int *i = sPtr1.get();
    cout << *i << endl;
    cout << *sPtr1 << endl << endl;
    
    *i = 200;
    cout << *i << endl;
    cout << *sPtr1 << endl << endl;
    
    *sPtr1 = 300;
    cout << *i << endl;
    cout << *sPtr1;
    return 0;
}
/** 輸出:
100

100
100

200
200

300
300
**/

直觀展示自動管理記憶體

// ...
int main()
{
    shared_ptr<Person> sPtr1 {make_shared<Person>()};
    shared_ptr<Person> sPtr2 {make_shared<Person>(18)};
    shared_ptr<Person> sPtr3 {make_shared<Person>(22)};
    
    shared_ptr<Person> sPtr4 = sPtr1;
    sPtr1.reset();
    sPtr2.reset();
    
    sPtr3.reset(new Person(19));
    
    cout << endl << "Before return" << endl;
    return 0;
}
/** 輸出:
Constructor: person's age = 0
Constructor: person's age = 18
Constructor: person's age = 22
Destructor: person's age = 18
Constructor: person's age = 19
Destructor: person's age = 22

Before return
Destructor: person's age = 0
Destructor: person's age = 19

**/

解釋:

  1. 定義了三個shared_ptrsPtr1sPtr2sPtr3,它們管理的對象分別為age = 0age = 18age = 22,列印了三行Person對象的構造函數中的輸出
  2. 通過賦值運算符,使得sPtr4同時管理sPtr1管理的對象
  3. 釋放sPtr1被管理對象的所有權,此時因為sPtr4還在管理原sPtr1管理的對象age = 0,因此age = 0對象並沒有被析構
  4. 釋放sPtr2被管理對象的所有權,此時因為age = 18對象沒有任何shared_ptr進行管理,age = 18對象被析構,列印了age = 18對象的析構函數中的輸出
  5. 釋放sPtr3被管理對象的所有權,sPtr3轉為管理age = 19的對象;是先構造age = 19對象,後再釋放sPtr3被管理對象的所有權,列印了age = 19對象的構造函數中的輸出;此時因為sPtr3原管理的age = 22對象沒有任何shared_ptr進行管理,age = 22對象被析構,列印了age = 22對象的析構函數中的輸出
  6. 在程式返回前Before return,管理age = 0age = 19對象的智能指針sPtr4sPtr3被析構,age = 0age = 19對象沒有智能指針管理,age = 0age = 19對象被析構,列印了它們對象的析構函數中的輸出。

別名

template< class Y >
shared_ptr( const shared_ptr<Y>& r, element_type* ptr ) noexcept;

別名用於訪問類的成員變數。

我們需要訪問的是某個實例的成員,因此並不希望在使用這個成員的時候,對應的實例被銷毀了。

使用別名的shared_ptr,增加了對這個實例的控制權,但仍然訪問的是成員。

// ...
struct Person{ int age = 18; };
struct Student{ Person person; };

int main(){
    shared_ptr<Student> studentPtr{make_shared<Student>()};
    cout << "studentPtr use count = " << studentPtr.use_count() << endl;
    shared_ptr<Person> personPtr{studentPtr, &(studentPtr->person)};
    cout << "studentPtr use count = " << studentPtr.use_count() << endl;
    cout << personPtr->age << endl;
    return 0;
}
/** 輸出:
studentPtr use count = 1
studentPtr use count = 2
18

**/

shared_ptr 與函數

按值傳遞

int main(){
	auto func = [](shared_ptr<int> sPtr){
        cout << "value = " << *sPtr << endl;
        cout << "enter func: use count = " << sPtr.use_count() << endl;
    };
    
    auto sPtr = make_shared<int>(100);
    cout << "init: use count = " << sPtr.use_count() << endl;
    func(sPtr);
    cout << "exit func: use count = " << sPtr.use_count() << endl;
    return 0;
}
/** 輸出:
init: use count = 1
value = 100
enter func: use count = 2
exit func: use count = 1

**/

按值傳遞,函數會複製一份參數,因此傳入的sPtr會被覆制一份,造成其對象的引用計數增加 1;
執行完這個函數,複製的那一份sPtr被銷毀,使得其對象的引用計數減少 1;

按引用傳遞

int main(){
	auto func = [](shared_ptr<int> &sPtr){
        cout << "value = " << *sPtr << endl;
        cout << "enter func: use count = " << sPtr.use_count() << endl;
    };
    
    auto sPtr = make_shared<int>(100);
    cout << "init: use count = " << sPtr.use_count() << endl;
    func(sPtr);
    cout << "exit func: use count = " << sPtr.use_count() << endl;
    return 0;
}
/** 輸出:
init: use count = 1
value = 100
enter func: use count = 1
exit func: use count = 1

**/

按引用傳遞,函數不會複製一份參數;
因此若函數內部無其它導致增加引用計數的操作,函數執行過程中引用計數都不會改變。

按引用傳遞,但為const

int main(){
    auto func = [](const shared_ptr<int> &sPtr){
        cout << "value = " << *sPtr << endl;
        cout << "enter func: use count = " << sPtr.use_count() << endl;
        sPtr.reset(); // error
        sPtr.reset(new Person()); // error
        sPtr.release(); // error
    };
    
    auto sPtr = make_shared<int>(100);
    cout << "init: use count = " << sPtr.use_count() << endl;
    func(sPtr);
    cout << "exit func: use count = " << sPtr.use_count() << endl;
    return 0;
}

使用const的引用傳遞,不能改變shared_ptr所管理的對象是哪一個,使用.reset()等都會造成編譯錯誤。

返回值為shared_ptr

int main(){
	auto createSPtr = [](int i) -> shared_ptr<Person>{
        shared_ptr<Person> sPtr = make_shared<Person>(i);
        cout << "age = " << i << " use count = " << sPtr.use_count() << endl;
        return sPtr;
    };
    shared_ptr<Person> sPtr = createSPtr(100);
    sPtr->getAge(); cout << "use count = " << sPtr.use_count() << endl;

    // 用作鏈式函數
    createSPtr(200)->getAge();
    
    cout << endl << "Before main return" << endl;
    return 0;
}
/** 輸出:
Constructor: person's age = 100
age = 100 use count = 1
Person's age = 100
use count = 1
Constructor: person's age = 200
age = 200 use count = 1
Person's age = 200
Destructor: person's age = 200

Before main return
Destructor: person's age = 100

**/

可見當用作鏈式函數時,使用完畢後,unique_ptr會被銷毀,同時被管理的對象也被析構。

管理動態數組需要指定刪除器

shared_ptr 的預設刪除器不支持釋放數組對象,需要指定刪除器。

例,一維數組指定刪除器

    // ...
    shared_ptr<int> ptr(new int[10], [](int* p) {delete[] p; });

同時,也可以使用 std::default_delete<T>() 函數作為刪除器,這個函數內部的刪除功能是通過delete釋放,T 為釋放什麼類型的記憶體的類型。

例,一維數組指定刪除器

    // ...
    shared_ptr<int> ptr(new int[10], default_delete<int[]>());

可以自己封裝模板函數來使 shared_ptr 支持釋放數組對象。

// ...
template <typename T>
shared_ptr<T> arrayShared_ptr(size_t size)
{
    // 返回匿名對象
    return shared_ptr<T>(new T[size], default_delete<T[]>());
}

int main()
{
    shared_ptr<int> sPtr1 = arrayShared_ptr<int>(100);
    shared_ptr<int> sPtr2 = arrayShared_ptr<char>(200);
    return 0;
}

危險行為

仍然可使用delete釋放智能指針管理的對象的地址

可以使用delete釋放shared_ptr管理的對象的地址,但是其它共用指針仍然可能會訪問這塊地址,若訪問了則會出現程式異常,因此應避免使用手動的delete

    // ...
    shared_ptr<int> sPtr1 {new int(100)};
    shared_ptr<int> sPtr2 = sPtr1;
    delete sPtr1.get();
    cout << *sPtr2 << endl; // error,運行時異常

同時存在原始指針和智能指針

如果一個地址記憶體,同時有原始指針和shared_ptr指向它,即使當所有shared_ptr都被銷毀,原始指針還依然存在的情況下,這個地址記憶體仍然會被釋放,若再用原始指針去訪問這個記憶體地址就是訪問了一塊未知地址的內容。

    // ...
	int *i1 = new int{100};
    shared_ptr<int> sPtr1{i1};
    int *i2 = sPtr1.get();
    cout << *i1 << endl;
    cout << *i2 << endl << endl;
    sPtr1.reset();
    cout << *i1 << endl; // 危險行為 
    cout << *i2 << endl; // 危險行為 
	return 0;
/** 輸出:
100
100

1664688 // 這是隨機的,訪問了一塊未知地址的內容
1664688 // 這是隨機的,訪問了一塊未知地址的內容

**/

同時,無論是使用shared_ptr,還是其它的智能指針,都應該避免與原始指針混用。

避免同時存在原始指針和智能指針的解決方案:

    // ...
    int *i = new int{100};
    shared_ptr<int> sPtr1{i};
    i = nullptr;
    delete i;
    cout << *sPtr1 << endl; // ok

使用一個原始指針初始化多個shared_ptr

不能使用一個原始指針初始化多個shared_ptr

    // ...
    int *i = new int{100};
    shared_ptr<int> sPtr1{i};
    shared_ptr<int> sPtr2{i}; // error 編譯通過 運行錯誤

最後

shared_ptr 由於使用引用計數,因此會造成額外的記憶體和性能開銷,因此在性能要求極為苛刻的情況下不適用。

unique_ptr是 0 開銷的智能指針,也能夠自動管理記憶體,但不會造成性能損失。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 抽獎程式 ''' 抽獎程式 使用時可以修改嘉賓名單,然後單機‘開始’和‘停止’按鈕 來控制界面上名單的滾動實現抽獎功能,涉及的模塊主要 有多線程 ''' import itertools import random import threading import time import tkinte ...
  • 摘要:本文主要講解圖像局部直方圖均衡化和自動色彩均衡化處理。這些演算法可以廣泛應用於圖像增強、圖像去噪、圖像去霧等領域。 本文分享自華為雲社區《[Python從零到壹] 五十四.圖像增強及運算篇之局部直方圖均衡化和自動色彩均衡化處理》,作者: eastmount。 一.局部直方圖均衡化 前文通過調用O ...
  • 簡介 限流顧名思義是對流量大小進行限制,防止請求數量超過系統的負載能力,導致系統崩潰,起到保護作用。 現實生活中限流也隨處可見,節假日出門旅行的人數會劇增,對於旅游景點來說往往會不堪重負,如果不進行人數控制,對整個景點的壓力會非常大,游客的體驗也會非常差,還容易出現安全事故等危險。 同樣的在一線城市 ...
  • 轉載:https://blog.csdn.net/tslx1020/article/details/128250777 1、spawn - 冷啟動 frida-trace -U -f com.apple.ExampleCode -m “+[NSURL URLWithString:]" 2、attac ...
  • 伺服器信息 在阿裡雲買了個搶占式的伺服器,地區為華南廣州,系統為Ubuntu 20.04,8核16GB。 安裝Docker 命令如下: $ apt-get update -y $ apt-get upgrade -y $ apt-get install -y docker.io 安裝成功後,檢查一下 ...
  • 2023-01-14 一、Spring底層IOC實現 1、IOC:將對象的控制器反轉給Spring 2、BeanFactory與ApplicationContext (1)BeanFactory:IOC容器的基本實現,是Spring內部的使用介面,是面向Spring本身的,不是提供給開發人員使用的。 ...
  • 牛牛剛剛出生,嗷嗷待哺,一開始他只能學說簡單的數字,你跟他說一個整數,他立刻就能學會。輸入一個整數,輸出這個整數。 ...
  • 本篇文章,我們就一起聊一聊如何來更好的使用緩存,探尋下如何降低緩存交互過程的性能損耗、如何壓縮緩存的存儲空間占用、如何保證多個操作命令原子性等問題的解決策略,讓緩存在項目中可以發揮出更佳的效果。 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 當別人做大數據用Java、Python的時候,我使用.NET做大數據、數據挖掘,這確實是值得一說的事。 寫的並不全面,但都是實際工作中的內容。 .NET在大數據項目中,可以做什麼? 寫腳本(使用控制台程式+頂級語句) 寫工具(使用Winform) 寫介面、寫服務 使用C#寫代碼的優點是什麼? ...
  • 前言 本文寫給想學C#的朋友,目的是以儘快的速度入門 C#好學嗎? 對於這個問題,我以前的回答是:好學!但仔細想想,不是這麼回事,對於新手來說,C#沒有那麼好學。 反而學Java還要容易一些,學Java Web就行了,就是SpringBoot那一套。 但是C#方向比較多,你是學控制台程式、WebAP ...
  • 某一日晚上上線,測試同學在回歸項目黃金流程時,有一個工單項目介面報JSF序列化錯誤,馬上升級對應的client包版本,編譯部署後錯誤消失。 線上問題是解決了,但是作為程式員要瞭解問題發生的原因和本質。但這都是為什麼呢? ...
  • 本文介紹基於Python語言中TensorFlow的Keras介面,實現深度神經網路回歸的方法。 1 寫在前面 前期一篇文章Python TensorFlow深度學習回歸代碼:DNNRegressor詳細介紹了基於TensorFlow tf.estimator介面的深度學習網路;而在TensorFl ...
  • 前段時間因業務需要完成了一個工作流組件的編碼工作。藉著這個機會跟大家分享一下整個創作過程,希望大家喜歡,組件暫且命名為"easyFlowable"。 接下來的文章我將從什麼是工作流、為什麼要自研這個工作流組件、架構設計三個維度跟大家來做個整體介紹。 ...
  • 1 簡介 我們之前使用了dapr的本地托管模式,但在生產中我們一般使用Kubernetes托管,本文介紹如何在GKE(GCP Kubernetes)安裝dapr。 相關文章: dapr本地托管的服務調用體驗與Java SDK的Spring Boot整合 dapr入門與本地托管模式嘗試 2 安裝GKE ...
  • 摘要:在jvm中有很多的參數可以進行設置,這樣可以讓jvm在各種環境中都能夠高效的運行。絕大部分的參數保持預設即可。 本文分享自華為雲社區《為什麼需要對jvm進行優化,jvm運行參數之標準參數》,作者:共飲一杯無。 我們為什麼要對jvm做優化? 在本地開發環境中我們很少會遇到需要對jvm進行優化的需 ...
  • 背景 我們的業務共使用11台(阿裡雲)伺服器,使用SpringcloudAlibaba構建微服務集群,共計60個微服務,全部註冊在同一個Nacos集群 流量轉發路徑: nginx->spring-gateway->業務微服務 使用的版本如下: spring-boot.version:2.2.5.RE ...
  • 基於php+webuploader的大文件分片上傳,帶進度條,支持斷點續傳(刷新、關閉頁面、重新上傳、網路中斷等情況)。文件上傳前先檢測該文件是否已上傳,如果已上傳提示“文件已存在”,如果未上傳則直接上傳。視頻上傳時會根據設定的參數(分片大小、分片數量)進行上傳,上傳過程中會在目標文件夾中生成一個臨 ...
  • 基於php大文件分片上傳至七牛雲,使用的是七牛雲js-sdk V2版本,引入js文件,配置簡單,可以暫停,暫停後支持斷點續傳(刷新、關閉頁面、重新上傳、網路中斷等情況),可以配置分片大小和分片數量,官方文檔https://developer.qiniu.com/kodo/6889/javascrip ...