比特币源码分析-多线程检查脚本

昕阳小编 105 0

比特币源码分析-多线程检查脚本-第1张图片-昕阳网

多线程脚本检查启动代码:

bool AppInitMain(Config Config,boost:thread_group threadGroup,cs scheduler scheduler){.if(nScriptCheckThreads){ for(int I=0;I nScriptCheckThreads-1;I){线程组。create _ thread(ThreadScriptCheck);} } .}静态CCheckQueue scriptcheckqueue(128);void线程脚本检查(){重命名线程(' bit coin-script ch ');scriptcheckqueue .thread();}

在AppInitMain中根据选项,创建多个线程。此处使用了促进的线程库,在绑定的线程函数ThreadScriptCheck中,调用一个全局状态的任务队列scriptcheckqueue。每个线程都去该队列中去任务,当队列中无任务可执行时,线程被条件变量阻塞

任务队列代码:

模板类ccheck队列{ private:boost:mutex mutex;boost:condition _ variable cond worker;升压:条件_变量condMasterstd:矢量队列;国际网络国际贸易;布尔法鲁克;无符号int nTodobool fQuit无符号int nBatchSizebool循环(bool f master=false);public: //!创建新的检查队列CCheckQueue(unsigned int nBatchSizeIn):nIdle(0),nTotal(0),fAllOk(true),nTodo(0),fQuit(false),nBatchSize(nBatchSizeIn){ } void Thread(){ Loop();}布尔等待(){返回循环(真);} void Add(STD:vector v checks){ boost:unique _ lock lock(互斥锁);for(T检查:v检查){ queue。push _ back(STD:move(check));} nto do=v检查。size();如果(v检查。size()==1){ cond worker。notify _ one();} else if(尚力财经小编2022v检查。size()1){ cond worker。notify _ all();} } bool IsIdle(){ boost:unique _ lock lock(互斥锁);return(n total==nIdle nTodo==0 fAllOk==true);} ~ CCheckQueue(){ } } bool CCheckQueue:Loop(bool f master=false){ boost:condition _ variable cond=f master?cond主:cond工;STD:vector v检查;vchecks储备(nBatchSize);unsigned int nNow=0;bool fOk=true do { { boost:unique _ lock lock(互斥锁);//首先进行前一次循环运行的清理(允许我们//在相同的批判教派中进行)if(nNow){ fAllOk=fOk;nTodo-=nNow;if (nTodo==0!fMaster) //我们处理了最后一个元素;通知掌握它//可以退出并返回结果康师傅。notify _ one();} else { nTotal} while(队列。empty()){ if((f master | | f quit)nTodo==0){ n total-;bool fRet=fAllOk//如果fAllOk=true,则稍后重置新工作的状态;返回品;} nIdlecond.wait(锁定);nIdle-;} nNow=std:max( 1U,std:min(nBatchSize,(无符号整数)队列。size()/(n total nIdle 1)));v型支票。调整大小(nNow);对于(无符号int I=0;我不知道;{ vChecks[i].交换(队列。back());排队。pop _ back();//将放到局部队列中的任务清除} fOk=fAllOk} //执行工作;执行本线程刚分到的工作for(T check:v checks){ 尚力财经小编2022 if(fOk)fOk=check();} v支票。clear();}而(真);}

使用解读:

- boost:互斥体互斥体;互斥锁保护内部的状态-boost:condition _ variable cond worker;在没有工作时,工作线程阻塞条件变量-boost:condition _ variable cond master;在没有工作时,主人线程阻塞条件变量- std:vector队列;要处理元素的队列-int nIdle;空闲的工作线程数量(包含主线程)-int nTotal;总的工作线程的数量,包含主线程-布尔法鲁克;临时评估结果-无符号int nTodo:还有多少验证任务没有完成,包括不在排队的,但仍在工作线程自己的批次中的任务数量-bool fQuit;是否需要退出-无符号int nBatchSize:每个批次最大的元素处理数量

队列中使用了模板类,执行的验证任务由T标识,T都必须提供一个重载的运算符()方法,并且反回一个布尔。默认为主线程推批量任务到队列中,其他的工作线程去处理这些任务,当主线程推完任务后,也去处理这些任务,直到任务队列全部处理完毕。 以上是队列的实现:主要任务处理在Loop()函数中;队列将进行两次调用来处理队列中的任务:

1。添加任务后:自动唤醒被阻塞的工作线程来处理添加的任务;请看:void add (std: vector vcheck) 2。主线程添加任务后,调用bool Wait()处理队列中的任务。处理完队列中的所有任务后,主线程退出。Void Add():批量向类的内部队列添加任务。此操作受锁保护,所有状态都将更新。

如果新增任务数为1,则只唤醒一个工作线程来处理;否则,唤醒所有工作线程。

RAII机制(资源获取即初始化)最早由比雅尼斯特劳斯特鲁普提出。解决这样一个问题:C中的

如果在这个程序段的末尾需要完成一些资源释放工作,正常情况下自然没有问题,但是当抛出异常时,释放资源的语句就不会被执行。于尚力财经小编2022是[比雅尼斯特劳斯特鲁普]认为保证资源释放代码能够运行的地方就是放在这个程序段(堆栈帧)中的对象的析构函数,因为堆栈缠绕会保证它们的析构函数都被执行。

将初始化和资源释放都移到一个包装类中的好处:

-保证资源的正常释放——省去了异常处理中冗长重复的清理逻辑,甚至有些可能还没有执行,从而保证了代码的异常安全。-简化代码量。

模板类CCheckQueueControl { private:CCheckQueue * p queue;bool fDonepublic:CCheckQueueControl(CCheckQueue * pqueueIn):p queue(pqueueIn),fDone(false) { if (pqueue!=nullptr){ bool isIdle=p queue-isIdle();assert(isIdle);} } bool Wait(){ if(p queue==nullptr)返回truebool fRet=p queue-Wait();fDone=true返回品;} void Add(STD:vector v checks){ if(p queue!=nullptr)p queue-Add(v checks);} ~CCheckQueueControl() { if(!fDone)Wait();} };

这个类主要用于管理CCheckQueue对象;采用RAII机制确保每次析构该类的对象时,CCheckQueue中的所有任务队列都得到处理。用于构建此对象的任务队列只能为零,或者队列中没有任务。因为创建的对象会调用任务队列的wait()方法来处理队列中的所有任务,然后退出。解释:

-当boolwait()处理完队列中的所有任务后,该方法退出并返回这些任务的处理结果-void Add()向CCheckQueue添加一个任务并唤醒子线程进行处理-~CCheckQueueControl()对象销毁,调用wait()方法确保处理完该队列中的所有任务

主链在块到来时被激活。使用了检查队列:

Static Bool Connect Block(const config config,const c block,CValidationState state,CBlockIndex *pindex,CCoinsViewCache view,const CChainParams chainparams,bool fJustCheck=false) {.CCheckQueueControl控件(fScriptChecks?script check queue:nullptr);for(size _ t I=0;I block . vtx . size();i ) {.如果(!tx。IsCoinBase()) { Amount fee=view。GetValueIn(tx) - tx。GetValueOut();nFees=费用。get satoshis();//如果我们实际上是在连接块,就不要缓存结果(不过还是//咨询缓存)。bool fCacheResults=fJustCheckSTD:vector v checks;如果(!CheckInputs(tx,state,view,fScriptChecks,flags,fCacheResults,fCacheResults,precomputdtransactiondata(tx),vChecks)) {返回错误(' ConnectBlock():对%s的CheckInputs失败,返回%s 'tx。GetId()。ToString()、FormatStateMessage(state));}控制。添加(v checks);} .} .}

ConnectBlock将该块链接到当前激活链,并更新UTXO集合。在该方法中,全局对象scriptcheckqueue用于构造临时管理对象,全局任务队列由该管理对象操作,以添加和执行任务。 当该临时的管理对象析构时,会调用等待()方法,加入任务处理,处理完所有任务后,该对象析构完成。CScriptCheck(根据每个交易输入构造的检查任务)

class CScriptCheck { private:CScript script pubkey;金额金额;const CTransaction * ptxTo无符号int ninuint 32 _ t nFlagsbool cacheStoreScriptError错误;预计算TransactionData tx数据;public: CScriptCheck() : amount(0),ptxTo(0),nIn(0),nFlags(0),cacheStore(false),error(SCRIPT_ERR_UNKNOWN_ERROR), txdata() {} CScriptCheck(const CScript &scriptPubKeyIn, const Amount amountIn, const CTransaction &txToIn, unsigned int nInIn, uint32_t nFlagsIn, bool cacheIn, const PrecomputedTransactionData &txdataIn) : scriptPubKey(scriptPubKeyIn), amount(amountIn), ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn), error(SCRIPT_ERR_UNKNOWN_ERROR), txdata(txdataIn) {} bool operator()(); void swap(CScriptCheck &check) { scriptPubKey.swap(check.scriptPubKey); std::swap(ptxTo, check.ptxTo); std::swap(amount, check.amount); std::swap(nIn, check.nIn); std::swap(nFlags, check.nFlags); std::swap(cacheStore, check.cacheStore); std::swap(error, check.error); std::swap(txdata, check.txdata); } ScriptError GetScriptError() const { return error; } };

代码解释:

- CScript scriptPubKey; 锁定脚本(即该验证交易的某个引用输出对应的锁定脚本)- Amount amount; 上述锁定脚本对应的金额(即花费的UTXO的金额)- const CTransaction *ptxTo; 正在花费的交易,即要检查的交易- unsigned int nIn; 要检查该交易的第几个输入;- uint32_t nFlags; 检查标识- ScriptError error; 验证出错的原因- bool operator()(); 此处重载了()运算符,执行脚本检查操作;

标签: boos

抱歉,评论功能暂时关闭!

微信号已复制,请打开微信添加咨询详情!