evpp設計細節系列(1):利用 enable_shared_from_this 實現一個自管理的定時器
0. 前言
現在我們覆盤一下這個功能的實現細節和演化過程。
1. 基礎程式碼
定時器原型宣告可能是下面的樣子:
class InvokeTimer {
public:
InvokeTimer(struct event_base* evloop, double timeout_ms, const std::function<void()>& f);
~InvokeTimer();
void Start();
};
這個是最基本的介面,可以設定一個仿函式,並設定一個過期時間,然後繫結到一個event_base
物件上,然後就可以期待過了一個預期的時間後,我們設定的仿函式被呼叫了。
為了便於說明後續的多個版本的實現,我們先將基礎的不變的程式碼說明一下。
基礎程式碼,我們採用evpp專案中的TimerEventWatcher
,詳細實現在這裡event_watcher.h和event_watcher.cc。它是一個時間定時器觀察者物件,可以觀察一個時間事件。
標頭檔案event_watcher.h
定義如下:
#pragma once
#include <functional>
struct event;
struct event_base;
namespace recipes {
class EventWatcher {
public:
typedef std::function<void ()> Handler;
virtual ~EventWatcher();
bool Init();
void Cancel();
void SetCancelCallback(const Handler& cb);
void ClearHandler() { handler_ = Handler(); }
protected:
EventWatcher(struct event_base* evbase, const Handler& handler);
bool Watch(double timeout_ms);
void Close();
void FreeEvent();
virtual bool DoInit() = 0;
virtual void DoClose() {}
protected:
struct event* event_;
struct event_base* evbase_;
bool attached_;
Handler handler_;
Handler cancel_callback_;
};
class TimerEventWatcher : public EventWatcher {
public:
TimerEventWatcher(struct event_base* evbase, const Handler& handler, double timeout_ms);
bool AsyncWait();
private:
virtual bool DoInit();
static void HandlerFn(int fd, short which, void* v);
private:
double timeout_ms_;
};
}
實現檔案event_watcher.cc
如下:
#include <string.h>
#include <assert.h>
#include <event2/event.h>
#include <event2/event_struct.h>
#include <event2/event_compat.h>
#include <iostream>
#include "event_watcher.h"
namespace recipes {
EventWatcher::EventWatcher(struct event_base* evbase, const Handler& handler)
: evbase_(evbase), attached_(false), handler_(handler) {
event_ = new event;
memset(event_, 0, sizeof(struct event));
}
EventWatcher::~EventWatcher() {
FreeEvent();
Close();
}
bool EventWatcher::Init() {
if (!DoInit()) {
goto failed;
}
::event_base_set(evbase_, event_);
return true;
failed:
Close();
return false;
}
void EventWatcher::Close() {
DoClose();
}
bool EventWatcher::Watch(double timeout_ms) {
struct timeval tv;
struct timeval* timeoutval = nullptr;
if (timeout_ms > 0) {
tv.tv_sec = long(timeout_ms / 1000);
tv.tv_usec = long(timeout_ms * 1000.0) % 1000;
timeoutval = &tv;
}
if (attached_) {
// When InvokerTimer::periodic_ == true, EventWatcher::Watch will be called many times
// so we need to remove it from event_base before we add it into event_base
if (event_del(event_) != 0) {
std::cerr << "event_del failed. fd=" << this->event_->ev_fd << " event_=" << event_ << std::endl;
// TODO how to deal with it when failed?
}
attached_ = false;
}
assert(!attached_);
if (event_add(event_, timeoutval) != 0) {
std::cerr << "event_add failed. fd=" << this->event_->ev_fd << " event_=" << event_ << std::endl;
return false;
}
attached_ = true;
return true;
}
void EventWatcher::FreeEvent() {
if (event_) {
if (attached_) {
event_del(event_);
attached_ = false;
}
delete (event_);
event_ = nullptr;
}
}
void EventWatcher::Cancel() {
assert(event_);
FreeEvent();
if (cancel_callback_) {
cancel_callback_();
cancel_callback_ = Handler();
}
}
void EventWatcher::SetCancelCallback(const Handler& cb) {
cancel_callback_ = cb;
}
TimerEventWatcher::TimerEventWatcher(struct event_base* evbase,
const Handler& handler,
double timeout_ms)
: EventWatcher(evbase, handler)
, timeout_ms_(timeout_ms) {}
bool TimerEventWatcher::DoInit() {
::event_set(event_, -1, 0, TimerEventWatcher::HandlerFn, this);
return true;
}
void TimerEventWatcher::HandlerFn(int /*fd*/, short /*which*/, void* v) {
TimerEventWatcher* h = (TimerEventWatcher*)v;
h->handler_();
}
bool TimerEventWatcher::AsyncWait() {
return Watch(timeout_ms_);
}
}
2. 一個最基本的實現:basic-01
我們先嚐試實現一個能滿足最基本需求的定時器。
// 標頭檔案
#include <memory>
#include <functional>
struct event_base;
namespace recipes {
class TimerEventWatcher;
class InvokeTimer;
class InvokeTimer {
public:
typedef std::function<void()> Functor;
InvokeTimer(struct event_base* evloop, double timeout_ms, const Functor& f);
~InvokeTimer();
void Start();
private:
void OnTimerTriggered();
private:
struct event_base* loop_;
double timeout_ms_;
Functor functor_;
std::shared_ptr<TimerEventWatcher> timer_;
};
}
// 實現檔案
#include "invoke_timer.h"
#include "event_watcher.h"
#include <thread>
#include <iostream>
namespace recipes {
InvokeTimer::InvokeTimer(struct event_base* evloop, double timeout_ms, const Functor& f)
: loop_(evloop), timeout_ms_(timeout_ms), functor_(f) {
std::cout << "InvokeTimer::InvokeTimer tid=" << std::this_thread::get_id() << " this=" << this << std::endl;
}
InvokeTimer::~InvokeTimer() {
std::cout << "InvokeTimer::~InvokeTimer tid=" << std::this_thread::get_id() << " this=" << this << std::endl;
}
void InvokeTimer::Start() {
std::cout << "InvokeTimer::Start tid=" << std::this_thread::get_id() << " this=" << this << std::endl;
timer_.reset(new TimerEventWatcher(loop_, std::bind(&InvokeTimer::OnTimerTriggered, this), timeout_ms_));
timer_->Init();
timer_->AsyncWait();
std::cout << "InvokeTimer::Start(AsyncWait) tid=" << std::this_thread::get_id() << " timer=" << timer_.get() << " this=" << this << " timeout(ms)=" << timeout_ms_ << std::endl;
}
void InvokeTimer::OnTimerTriggered() {
std::cout << "InvokeTimer::OnTimerTriggered tid=" << std::this_thread::get_id() << " this=" << this << std::endl;
functor_();
functor_ = Functor();
}
}
測試main.cc
#include "invoke_timer.h"
#include "event_watcher.h"
#include "winmain-inl.h"
#include <event2/event.h>
void Print() {
std::cout << __FUNCTION__ << " hello world." << std::endl;
}
int main() {
struct event_base* base = event_base_new();
auto timer = new recipes::InvokeTimer(base, 1000.0, &Print);
timer->Start();
event_base_dispatch(base);
event_base_free(base);
delete timer;
return 0;
}
我們先建立一個event_base
物件,隨後建立一個InvokeTimer
物件,隨後讓timer啟動起來,即將timer註冊到event_base
物件中,最後執行event_base_dispatch(base)
。
下面編譯執行,結果是符合預期的:當timer的時間到期後,能順利觸發回撥。
$ ls -l
total 80
-rw-rw-r-- 1 weizili weizili 2729 Apr 15 20:39 event_watcher.cc
-rw-rw-r-- 1 weizili weizili 996 Apr 15 20:39 event_watcher.h
-rw-rw-r-- 1 weizili weizili 1204 Apr 14 10:55 invoke_timer.cc
-rw-rw-r-- 1 weizili weizili 805 Apr 14 10:55 invoke_timer.h
-rw-rw-r-- 1 weizili weizili 374 Apr 14 10:55 main.cc
$ g++ -std=c++11 event_watcher.cc invoke_timer.cc main.cc -levent
$ ./a.out
InvokeTimer::InvokeTimer tid=139965845526336 this=0x7ffd2790f780
InvokeTimer::Start tid=139965845526336 this=0x7ffd2790f780
InvokeTimer::Start(AsyncWait) tid=139965845526336 timer=0x14504c0 this=0x7ffd2790f780 timeout(ms)=1000
InvokeTimer::OnTimerTriggered tid=139965845526336 this=0x7ffd2790f780
Print hello world.
InvokeTimer::~InvokeTimer tid=139965845526336 this=0x7ffd2790f780
這個實現方式,InvokeTimer
物件生命週期的管理是一個問題,它需要呼叫者自己管理。
3. 能夠實現最基本自我管理:basic-02
為了實現InvokeTimer
物件生命週期的自我管理,其實就是呼叫者不需要關心InvokeTimer
物件的生命週期問題。可以設想一下,假如InvokeTimer
物件建立後,當定時時間一到,我們就呼叫其繫結的毀掉回函,然後InvokeTimer
物件自我銷燬,是不是就可以實現自我管理了呢?嗯,這個可行。請看下面程式碼。
// 標頭檔案
#include <memory>
#include <functional>
struct event_base;
namespace recipes {
class TimerEventWatcher;
class InvokeTimer;
class InvokeTimer {
public:
typedef std::function<void()> Functor;
static InvokeTimer* Create(struct event_base* evloop,
double timeout_ms,
const Functor& f);
~InvokeTimer();
void Start();
private:
InvokeTimer(struct event_base* evloop, double timeout_ms, const Functor& f);
void OnTimerTriggered();
private:
struct event_base* loop_;
double timeout_ms_;
Functor functor_;
std::shared_ptr<TimerEventWatcher> timer_;
};
}
// 實現檔案
#include "invoke_timer.h"
#include "event_watcher.h"
#include <thread>
#include <iostream>
namespace recipes {
InvokeTimer::InvokeTimer(struct event_base* evloop, double timeout_ms, const Functor& f)
: loop_(evloop), timeout_ms_(timeout_ms), functor_(f) {
std::cout << "InvokeTimer::InvokeTimer tid=" << std::this_thread::get_id() << " this=" << this << std::endl;
}
InvokeTimer* InvokeTimer::Create(struct event_base* evloop, double timeout_ms, const Functor& f) {
return new InvokeTimer(evloop, timeout_ms, f);
}
InvokeTimer::~InvokeTimer() {
std::cout << "InvokeTimer::~InvokeTimer tid=" << std::this_thread::get_id() << " this=" << this << std::endl;
}
void InvokeTimer::Start() {
std::cout << "InvokeTimer::Start tid=" << std::this_thread::get_id() << " this=" << this << std::endl;
timer_.reset(new TimerEventWatcher(loop_, std::bind(&InvokeTimer::OnTimerTriggered, this), timeout_ms_));
timer_->Init();
timer_->AsyncWait();
std::cout << "InvokeTimer::Start(AsyncWait) tid=" << std::this_thread::get_id() << " timer=" << timer_.get() << " this=" << this << " timeout(ms)=" << timeout_ms_ << std::endl;
}
void InvokeTimer::OnTimerTriggered() {
std::cout << "InvokeTimer::OnTimerTriggered tid=" << std::this_thread::get_id() << " this=" << this << std::endl;
functor_();
functor_ = Functor();
delete this;
}
}
請注意,上述實現中,為了實現自我銷燬,我們必須呼叫 delete ,這就註定了InvokeTimer
物件必須在堆上建立,因此我們隱藏了它的建構函式,然後用一個靜態的 Create 成員來建立InvokeTimer
物件的例項。
相應的,main.cc
也做了一點點修改程式碼如下:
#include "invoke_timer.h"
#include "event_watcher.h"
#include "winmain-inl.h"
#include <event2/event.h>
void Print() {
std::cout << __FUNCTION__ << " hello world." << std::endl;
}
int main() {
struct event_base* base = event_base_new();
auto timer = recipes::InvokeTimer::Create(base, 1000.0, &Print);
timer->Start(); // 啟動完成後,就不用關注該物件了
event_base_dispatch(base);
event_base_free(base);
return 0;
}
這個實現,就不需要上層呼叫者手工delete
這個InvokeTimer
物件的例項,從而達到InvokeTimer
物件自我管理的目的。
下面編譯執行,結果是符合預期的:當timer時間到期後,能順利觸發回撥,並且InvokeTimer
物件也自動析構了。
$ ls -l
total 80
-rw-rw-r-- 1 weizili weizili 2729 Apr 15 20:39 event_watcher.cc
-rw-rw-r-- 1 weizili weizili 996 Apr 15 20:39 event_watcher.h
-rw-rw-r-- 1 weizili weizili 1204 Apr 14 10:55 invoke_timer.cc
-rw-rw-r-- 1 weizili weizili 805 Apr 14 10:55 invoke_timer.h
-rw-rw-r-- 1 weizili weizili 374 Apr 14 10:55 main.cc
$ g++ -std=c++11 event_watcher.cc invoke_timer.cc main.cc -levent
$ ./a.out
InvokeTimer::InvokeTimer tid=139965845526336 this=0x7ffd2790f780
InvokeTimer::Start tid=139965845526336 this=0x7ffd2790f780
InvokeTimer::Start(AsyncWait) tid=139965845526336 timer=0x14504c0 this=0x7ffd2790f780 timeout(ms)=1000
InvokeTimer::OnTimerTriggered tid=139965845526336 this=0x7ffd2790f780
Print hello world.
InvokeTimer::~InvokeTimer tid=139965845526336 this=0x7ffd2790f780
4. 如果要取消一個定時器怎麼辦:cancel-03
上面第2種實現方式,實現了定時器的自我管理,呼叫者不需要關心定時器的生命週期的管理問題。接下來,新的需求又來了,上層呼叫者說,在對外發起一個請求時,可以設定一個定時器來處理超時問題,但如果請求及時的回來了,我們得及時取消該定時器啊,這又如何處理呢?
這就相當於要把上層呼叫者還得一直保留InvokeTimer
物件的例項,以便在需要的時候,提前取消掉該定時器。上層呼叫者保留這個指標,就會帶來一定的風險,例如誤用,當InvokeTimer
物件已經自動析構了,該該指標還繼續存在於上層呼叫者那裡。
於是乎,智慧指標shared_ptr
出場了,我們希望上層呼叫者看到的物件是以shared_ptr<InvokeTimer>
形式存在的,無論上層呼叫者是否保留這個shared_ptr<InvokeTimer>
物件,InvokeTimer
物件都能自我管理,也就是說,當上層呼叫者不保留shared_ptr<InvokeTimer>
物件時,InvokeTimer
物件要能自我管理。
這裡就必須讓InvokeTimer
物件本身也要儲存一份shared_ptr<InvokeTimer>
物件。為了實現這一技術,我們需要引入enable_shared_from_this
。關於enable_shared_from_this
的介紹,網路上已經有很多資料了,這裡不多累述。我們直接上最終的實現程式碼:
// 標頭檔案
#include <memory>
#include <functional>
struct event_base;
namespace recipes {
class TimerEventWatcher;
class InvokeTimer;
typedef std::shared_ptr<InvokeTimer> InvokeTimerPtr;
class InvokeTimer : public std::enable_shared_from_this<InvokeTimer> {
public:
typedef std::function<void()> Functor;
static InvokeTimerPtr Create(struct event_base* evloop,
double timeout_ms,
const Functor& f);
~InvokeTimer();
void Start();
void Cancel();
void set_cancel_callback(const Functor& fn) {
cancel_callback_ = fn;
}
private:
InvokeTimer(struct event_base* evloop, double timeout_ms, const Functor& f);
void OnTimerTriggered();
void OnCanceled();
private:
struct event_base* loop_;
double timeout_ms_;
Functor functor_;
Functor cancel_callback_;
std::shared_ptr<TimerEventWatcher> timer_;
std::shared_ptr<InvokeTimer> self_; // Hold myself
};
}
// 實現檔案
#include "invoke_timer.h"
#include "event_watcher.h"
#include <thread>
#include <iostream>
namespace recipes {
InvokeTimer::InvokeTimer(struct event_base* evloop, double timeout_ms, const Functor& f)
: loop_(evloop), timeout_ms_(timeout_ms), functor_(f) {
std::cout << "InvokeTimer::InvokeTimer tid=" << std::this_thread::get_id() << " this=" << this << std::endl;
}
InvokeTimerPtr InvokeTimer::Create(struct event_base* evloop, double timeout_ms, const Functor& f) {
InvokeTimerPtr it(new InvokeTimer(evloop, timeout_ms, f));
it->self_ = it;
return it;
}
InvokeTimer::~InvokeTimer() {
std::cout << "InvokeTimer::~InvokeTimer tid=" << std::this_thread::get_id() << " this=" << this << std::endl;
}
void InvokeTimer::Start() {
std::cout << "InvokeTimer::Start tid=" << std::this_thread::get_id() << " this=" << this << " refcount=" << self_.use_count() << std::endl;
timer_.reset(new TimerEventWatcher(loop_, std::bind(&InvokeTimer::OnTimerTriggered, shared_from_this()), timeout_ms_));
timer_->SetCancelCallback(std::bind(&InvokeTimer::OnCanceled, shared_from_this()));
timer_->Init();
timer_->AsyncWait();
std::cout << "InvokeTimer::Start(AsyncWait) tid=" << std::this_thread::get_id() << " timer=" << timer_.get() << " this=" << this << " refcount=" << self_.use_count() << " periodic=" << periodic_ << " timeout(ms)=" << timeout_ms_ << std::endl;
}
void InvokeTimer::Cancel() {
if (timer_) {
timer_->Cancel();
}
}
void InvokeTimer::OnTimerTriggered() {
std::cout << "InvokeTimer::OnTimerTriggered tid=" << std::this_thread::get_id() << " this=" << this << " use_count=" << self_.use_count() << std::endl;
functor_();
functor_ = Functor();
cancel_callback_ = Functor();
timer_.reset();
self_.reset();
}
void InvokeTimer::OnCanceled() {
std::cout << "InvokeTimer::OnCanceled tid=" << std::this_thread::get_id() << " this=" << this << " use_count=" << self_.use_count() << std::endl;
if (cancel_callback_) {
cancel_callback_();
cancel_callback_ = Functor();
}
functor_ = Functor();
timer_.reset();
self_.reset();
}
}
相應的,main.cc
也做了一點點修改程式碼如下:
#include "invoke_timer.h"
#include "event_watcher.h"
#include "winmain-inl.h"
#include <event2/event.h>
void Print() {
std::cout << __FUNCTION__ << " hello world." << std::endl;
}
int main() {
struct event_base* base = event_base_new();
auto timer = recipes::InvokeTimer::Create(base, 1000.0, &Print);
timer->Start(); // 啟動完成後,就不用關注該物件了
event_base_dispatch(base);
event_base_free(base);
return 0;
}
這個實現,就不需要上層呼叫者手工delete
這個InvokeTimer
物件的例項,從而達到InvokeTimer
物件自我管理的目的。
下面編譯執行,結果是符合預期的:當timer時間到期後,能順利觸發回撥,並且InvokeTimer
物件也自動析構了。
5. 實現一個週期性的定時器:periodic-04
上述幾個實現中,都是一次性的定時器任務。但是如果我們想實現一個週期性的定時器該如何實現呢?例如,我們有一個任務,需要每分鐘做一次。
其實,基於上述第三個版本的實現,可以很容易的實現週期性的定時器功能。只需要在回撥函式中,繼續呼叫timer->AsyncWait()
即可。詳細的修改情況如下。
標頭檔案 invoke_timer.h 改變:
@@ -18,7 +18,8 @@ public:
static InvokeTimerPtr Create(struct event_base* evloop,
double timeout_ms,
- const Functor& f);
+ const Functor& f,
+ bool periodic);
~InvokeTimer();
@@ -30,7 +31,7 @@ public:
cancel_callback_ = fn;
}
private:
- InvokeTimer(struct event_base* evloop, double timeout_ms, const Functor& f);
+ InvokeTimer(struct event_base* evloop, double timeout_ms, const Functor& f, bool periodic);
void OnTimerTriggered();
void OnCanceled();
@@ -40,6 +41,7 @@ private:
Functor functor_;
Functor cancel_callback_;
std::shared_ptr<TimerEventWatcher> timer_;
+ bool periodic_;
std::shared_ptr<InvokeTimer> self_; // Hold myself
};
實現檔案 invoke_timer.cc 改變:
namespace recipes {
-InvokeTimer::InvokeTimer(struct event_base* evloop, double timeout_ms, const Functor& f)
- : loop_(evloop), timeout_ms_(timeout_ms), functor_(f) {
+InvokeTimer::InvokeTimer(struct event_base* evloop, double timeout_ms, const Functor& f, bool periodic)
+ : loop_(evloop), timeout_ms_(timeout_ms), functor_(f), periodic_(periodic) {
std::cout << "InvokeTimer::InvokeTimer tid=" << std::this_thread::get_id() << " this=" << this << std::endl;
}
-InvokeTimerPtr InvokeTimer::Create(struct event_base* evloop, double timeout_ms, const Functor& f) {
- InvokeTimerPtr it(new InvokeTimer(evloop, timeout_ms, f));
+InvokeTimerPtr InvokeTimer::Create(struct event_base* evloop, double timeout_ms, const Functor& f, bool periodic) {
+ InvokeTimerPtr it(new InvokeTimer(evloop, timeout_ms, f, periodic));
it->self_ = it;
return it;
}
@@ -27,7 +27,7 @@ void InvokeTimer::Start() {
timer_->SetCancelCallback(std::bind(&InvokeTimer::OnCanceled, shared_from_this()));
timer_->Init();
timer_->AsyncWait();
}
void InvokeTimer::Cancel() {
@@ -39,14 +39,20 @@ void InvokeTimer::Cancel() {
void InvokeTimer::OnTimerTriggered() {
std::cout << "InvokeTimer::OnTimerTriggered tid=" << std::this_thread::get_id() << " this=" << this << " use_count=" << self_.use_count() << std::endl;
functor_();
- functor_ = Functor();
- cancel_callback_ = Functor();
- timer_.reset();
- self_.reset();
+
+ if (periodic_) {
+ timer_->AsyncWait();
+ } else {
+ functor_ = Functor();
+ cancel_callback_ = Functor();
+ timer_.reset();
+ self_.reset();
+ }
}
void InvokeTimer::OnCanceled() {
std::cout << "InvokeTimer::OnCanceled tid=" << std::this_thread::get_id() << " this=" << this << " use_count=" << self_.use_count() << std::endl;
+ periodic_ = false;
if (cancel_callback_) {
cancel_callback_();
cancel_callback_ = Functor();
main.cc測試示例程式碼也有所修改,具體如下:
#include "invoke_timer.h"
#include "event_watcher.h"
#include "winmain-inl.h"
#include <event2/event.h>
void Print() {
std::cout << __FUNCTION__ << " hello world." << std::endl;
}
int main() {
struct event_base* base = event_base_new();
auto timer = recipes::InvokeTimer::Create(base, 1000.0, &Print, true);
timer->Start();
timer.reset();
event_base_dispatch(base);
event_base_free(base);
return 0;
}
6. 最後
7. evpp系列文章列表
相關推薦
evpp設計細節系列(1):利用 enable_shared_from_this 實現一個自管理的定時器
0. 前言 現在我們覆盤一下這個功能的實現細節和演化過程。 1. 基礎程式碼 定時器原型宣告可能是下面的樣子: class InvokeTimer { public: InvokeTimer(struct event_base* ev
WebRTC系列(1)-手把手教你實現一個瀏覽器拍照室Demo
1.WebRTC開發背景 由於業務需求,需要在專案中實現實時音視訊通話功能,之前基於瀏覽器開發的Web專案要進行音視訊通話,需要安裝flash外掛才能實現或者使用C/S客戶端進行通訊。隨著網際網路技術的驅動下,在很多場景下需要進行音視訊通訊,在生活中我們現在使用電話越來越少,使用微信和視訊越來越多。在一
.NET Core微服務之路:利用DotNetty實現一個簡單的通訊過程
上一篇我們已經全面的介紹過《基於gRPC服務發現與服務治理的方案》,我們先複習一下RPC的呼叫過程(筆者會在這一節的幾篇文章中反覆的強調這個過程呼叫方案),看下圖
從0到1構建大數據生態系列1:數據蠻荒中的拓荒之舉
市場需求 ont 應用 load 工作 網站 做事 做到 實施 緣起 我們都知道,當前大數據的需求基本屬於遍地開花。無論是帝都、魔都,還是廣州、深圳,亦或是全國其他各地,都在搞大數據;不管是不到百人的微小公司,還是幾百上千人的中型公司,亦或是上萬的大型公司,都在需求
設計模式筆記1:簡單工廠模式
1.3 簡單 修改 作用 面向對象 對象 面向 tro 計算 如果想成為一名更優秀的軟件設計師,了解優秀軟件設計的演變過程比學習優秀設計本身更有價值。 1.1 面向對象的好處 通過封裝、繼承多態把程序的耦合度降低,使用設計模式使得程序更加靈活,容易修改,易於復用
Azure Stack技術深入淺出系列1:Azure Stack與Azure的有QoS保證的網絡聯通實現方法和對比測試
azure stack 雲計算 微軟 azure源自Azure的Azure stack作為一款業界唯一的和領先的公有雲平臺一致的混合雲平臺,能夠幫助企業客戶從自有數據中心交付Azure雲服務。它作為微軟混合雲戰略中的重頭戲,官方宣稱其將在今年年中GA了。上海儀電集團高度重視這一產品,同時成立了一個專門的團隊來
HTML+CSS專案課1:利用table製作百度首頁
知識點:html文件基本結構、table標籤佈局、在單元格中插入文字、圖片、連結、表單。 網頁效果圖: 製作思路:將整個網頁當做一個table表格 1、製作一個6行1列的表格 2、在單元格中插入相關內容 3、在第4行的單元格里插入一個form表單 <!DOCT
『PHP學習筆記』系列一:利用for迴圈解決過路口問題
過路口問題: 假設某人有100,000現金。每經過一次路口需要進行一次交費。交費規則為當他現金大於50,000時每次需要交5%如果現金小於等於50,000時每次交5,000。請寫一程式計算此人可以經過多少次這個路口。 解題思路: 此題最重要的其實就是思路和邏輯,程式碼實現其實很簡單,這裡
『PHP學習筆記』系列四:利用函式遞迴呼叫思想解決【斐波那契數列】問題和【猴子吃桃問題】問題
什麼是函式遞迴思想? 遞迴思想:把一個相對複雜的問題,轉化為一個與原問題相似的,且規模較小的問題來求解。 遞迴方法只需少量的程式就可描述出解題過程所需要的多次重複計算,大大地減少了程式的程式碼量。 但在帶來便捷的同時,也會有一些缺點,函式遞迴的執行效率不高(多次呼叫時)。
『PHP學習筆記』系列九:利用from表單的onSubmit事件進行瀏覽器端的資料驗證
資料驗證思路: 當我們在網站進行註冊時,一般有兩個資料驗證的過程,一個是在伺服器端的驗證,一個是在瀏覽器端的驗證。瀏覽器端的驗證一般是用來驗證提交的資訊是否符合註冊的要求,即資料是否合法;伺服器端的驗證主要是驗證該註冊資訊是否已經存在於伺服器中,如果註
Linux從0到1②:利用Xshell遠端管理
一、管理方式 1. 遠端登陸shell 兩種輸入方式: 輸入命令:效率低,適合少量的工作 Shell Script:效率高,適合完成複雜,重複性的工作 2.本地管理 開啟本地虛擬機器進行管理 注:推薦使用XShell 二、XShell
Net程式設計師學用Oracle系列(1):導航目錄
原文:https://www.cnblogs.com/hanzongze/p/oracle-catalog.html .Net程式設計師學用Oracle系列(1):導航目錄 .Net程式設計師學用Oracle系列(2):準備測試環境 .Net程式設計師學用Oracle系列(3):資料庫程式設計
em算法系列1:python實現三硬幣模型
三硬幣模型 假設有3枚硬幣,分別記做A,B,C。這些硬幣正面出現的概率分別是π,p和q。進行如下擲硬幣實驗:先擲硬幣A,根據其結果選出硬幣B或C,正面選B,反面選硬幣C;然後投擲選重中的硬幣,出現正面記作1,反面記作0;獨立地重複n次(n=10),結果為1111
Hibernate系列1:入門程式
1.傳統的java資料庫連線 在傳統的開發中,如果要建立java程式和資料庫的連線,通常採用JDBC或者Apache Commons DbUtils開發包來完成。他們分別有以下特點: JDBC: 優點:1.底層連線,效率高 缺點:需要手寫sql語句,程式碼重複多,封裝結果集繁
1、利用介面實現動態的建立物件[選做題] 1.1 建立4個類: 蘋果 香蕉 葡萄 園丁 1.2 在三種水果的構造方法中列印一句話. 以蘋果類為例
package javademo9; import java.util.Scanner; interface Fruit{ } class Apple implements Fruit { public Apple() { System.out.println("建立了一個蘋
SOFAMesh中的多協議通用解決方案x-protocol介紹系列(1) : DNS通用定址方案
小螞蟻說: 2018年上半年,螞蟻金服決定基於 Istio 訂製自己的 ServiceMesh 解決方案,並在6月底正式對外公佈了 SOFAMesh 。 在 SOFAMesh 的開發過程中,針對遇到的實際問題,我們給出了一套名為 x-protocol 的解決方案,本文將會對這個解決方案進
搜尋引擎系列1:什麼是正向索引?什麼是倒排索引?
什麼是正向索引、什麼是倒排索引? 正向索引(forward index),反向索引(inverted index)更熟悉的名字是倒排索引。 在搜尋引擎中每個檔案都對應一個檔案ID,檔案內容被表示為一
設計模式系列1——單例模式
設計模式是一種被重用的程式碼模式,主要大類分為三種:建立型模式、結構型模式、行為型模式。 單例模式的含義是:對於定義的一個類,在整個應用程式執行期間只有唯一的一個例項物件,這樣的設計模式叫做單例模式,單例模式分為餓漢式和懶漢式兩種。 一、懶漢式 懶漢式的特點是當需要用到此單
Oracle高併發系列1:DML引起的常見問題及優化思路
引言 Oracle資料庫是設計為一個高度共享的資料庫,這裡所說的“共享”,可以從資料庫共享記憶體、後臺程序、cursor、執行計劃、latch等方面去理解。Oracle如此設計的目的是以最小的系統開銷、最大化地支援更多的併發會話。也是基於這個設計思想,所以Oracle單個例項的垂直擴充套件能力一直
強化學習系列1:強化學習簡介
2015年10月,AlphaGo在和歐洲冠軍進行的圍棋賽上獲得了5:0的完勝,其後的深度強化學習也隨之火了起來。從本期開始開個新坑,一步步把強化學習的內容捋一遍。 1. 基本概念 強化學習(reinforcement learning)是用來解決連續決策問題的一種方法。針對的模型是馬