所用到的都是ASP讀寫數(shù)據(jù)庫操作.
創(chuàng)新互聯(lián)建站2013年至今,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項(xiàng)目網(wǎng)站設(shè)計(jì)制作、做網(wǎng)站網(wǎng)站策劃,項(xiàng)目實(shí)施與項(xiàng)目整合能力。我們以讓每一個(gè)夢(mèng)想脫穎而出為使命,1280元洞口做網(wǎng)站,已為上家服務(wù),為洞口各地企業(yè)和個(gè)人服務(wù),聯(lián)系電話:028-86922220
沒有具體的規(guī)則.
現(xiàn)在給你些資料
一.頁面設(shè)計(jì)部分
1.img控件
alt:所有展示類圖片都要具有能簡要描述圖片內(nèi)容的文字說明。
2.Input控件
maxlength:所有INPUT控件都需要制定maxlength屬性,默認(rèn)值為數(shù)據(jù)庫中對(duì)應(yīng)的字段的長度。
readonly:所有不可更改的信息都要使用readonly屬性。
3.Form控件
action:所有Form都要指定action,如果提交給本身就指定action=""
method:執(zhí)行不可逆動(dòng)作使用POST,可逆動(dòng)作使用GET
onsubmit:所有form都要指定提交前需要的檢查程序。
所有form都要有對(duì)應(yīng)的reset button。
4.button控件
onclick:form中用于提交的button不容許使用此方法,所有數(shù)據(jù)檢查通過form的onsubmit激活。
5.title屬性
所有頁面都要具有和本頁標(biāo)題相同的title。
6.控件的命名
采用控件類型縮寫(小寫)+英文單詞(第一個(gè)字母大寫)的方法。
開發(fā)中控件基本涉及一下幾類
button:btn
form:frm
select:sel
textarea:txt
input:ipt
7.語言設(shè)置
所有中文頁面都要加上如下語句:
meta http-equiv="Content-Language" content="zh-cn"
meta http-equiv="Content-Type" content="text/html; charset=gb2312"
8.控件屬性賦值
所有控件的屬性值都要使用雙引號(hào)或者單引號(hào)包括起來。
二.客戶端程序部分
1.錯(cuò)誤提示信息的處理(2-1)
所有錯(cuò)誤信息全部使用中文提示錯(cuò)誤信息,標(biāo)點(diǎn)使用中文半角符號(hào),格式如下:
"錯(cuò)誤:"+提示信息+"!"
2.成功提示信息的處理(2-2)
所有成功信息全部使用中文提示成功信息,標(biāo)點(diǎn)使用中文半角符號(hào),格式如下:
"成功:"+提示信息+"!"
3.頁面的返回
所有需要返回上一頁的時(shí)候使用history.back();不使用history.go(-1);
需要返回前n頁(n1)時(shí)使用history.go(-n);
所有返回都使用連接的方式而不是button。
4.提交前數(shù)據(jù)的判斷
保證提交前的數(shù)據(jù)都會(huì)通過JavaScript進(jìn)行數(shù)據(jù)類型以及長度的判斷
是否為數(shù)字:使用函數(shù)isNaN()
長度判斷: 長度要判斷去掉前后空格后的實(shí)際長度
為空判斷: 所有不容許為空的輸入字段都要在去掉前后空格后進(jìn)行判斷,同時(shí)如果該字段為查詢條件則必須不能為空
如果判斷條件發(fā)現(xiàn)數(shù)據(jù)錯(cuò)誤,則通過(2-1)提示錯(cuò)誤信息,然后通過方法focus()聚焦錯(cuò)誤字段。
5.刪除數(shù)據(jù)前的提示
所有涉及刪除的操作,在用戶選定以后都要再進(jìn)行一次確認(rèn)操作。
三.服務(wù)器端程序部分
1.數(shù)據(jù)的取得
通過Get,Post,連接傳遞過來的數(shù)據(jù)在使用前都要通過trim去掉數(shù)據(jù)前后的空格。
2.數(shù)據(jù)的判斷
通過request的得到的參數(shù)數(shù)據(jù)需要再次進(jìn)行空,類型,和長度的判斷。
3.對(duì)象的關(guān)閉
所有數(shù)據(jù)庫和文件對(duì)象都要在使用后盡可能早的close,同時(shí)賦nothing。
4.提示信息
所有錯(cuò)誤提示信息使用JavaScript提示,保證使用者看不到任何內(nèi)部錯(cuò)誤信息。(如1-1)
涉及數(shù)據(jù)庫Update,Del,Insert的操作成功都要提示。(如1-2)
5.變量的使用
所有變量在使用前都需要聲明,并且賦初值。
6.變量的命名
采用變量類型縮寫(小寫)+英文單詞(第一個(gè)字母大寫)的方法。
開發(fā)中變量基本涉及一下幾類
整數(shù):i
小數(shù):f
字符: s
布爾:b
日期:d
特殊的:
循環(huán)依次采用i,j,m,n;
數(shù)組用ary
指針p,q
臨時(shí)變量tmp
七.SQL語句
1.排序
order時(shí)應(yīng)該盡量提前使用建立索引或者主鍵的字段排序。
2.select
select時(shí)避免使用*,即使需要所有字段也應(yīng)盡量一個(gè)一個(gè)按照使用的順序羅列出來。
3.盡量避免使用in和not in
八.測(cè)試
所有頁面要在800*600,1024*768兩種分辨率下運(yùn)行通過。
所有頁面要在IE5.0,5.5以及6.0下運(yùn)行通過沒有JavaScript錯(cuò)誤。
****************************************************************
WEB編碼規(guī)范
編制人:walaqi
第一章 ASP編碼規(guī)范通述
ASP編碼分為兩大部分,一部分為靜態(tài)文件編碼,一部分為包含服務(wù)器端腳本的動(dòng)態(tài)文件編碼。
靜態(tài)文件編碼分Script編碼和HTML編碼兩部分。
服務(wù)器端編碼則分為服務(wù)器腳本、客戶端腳本、HTML腳本三部分。
編碼規(guī)范采用如下約定:
所有客戶端腳本一律使用JavaScript
所有服務(wù)器端腳本一律使用VBScript
靜態(tài)頁面輸出一律使用HTML腳本
本規(guī)范不適用于由服務(wù)器端腳本所產(chǎn)生的客戶端腳本代碼。
第二章 靜態(tài)文件編碼規(guī)范:
靜態(tài)文件腳本部分采用JavaScript編寫。輸出部分采用HTML標(biāo)記語言。
1. HTML標(biāo)記語言編碼規(guī)范
1.1 標(biāo)記的換行規(guī)范:
* 一個(gè)標(biāo)記必須占用一行。不得出現(xiàn)兩個(gè)標(biāo)記在同一行的情況(同一標(biāo)記的關(guān)閉標(biāo)記除外),如:
trtdtext/td/tr
而必須寫成:
tr
tdtext/td
tr
1.2 標(biāo)記的關(guān)閉規(guī)范
* 靜態(tài)文件內(nèi)容必須包含在body/body標(biāo)記中間
* body標(biāo)記必須包含在html/html標(biāo)記中間
* 對(duì)于需要關(guān)閉的標(biāo)記,如:
htmltitlebodytabletrtdptextareaselectfontoptiondivspan
必須同其關(guān)閉標(biāo)記同時(shí)出現(xiàn)。如
body…p…font…./font…./p…../body
* 不得出現(xiàn)交叉包含的語句,如:
pfont…../p/font
1.3 標(biāo)記的屬性賦值規(guī)范
對(duì)于接受屬性的標(biāo)記,屬性值必須使用雙引號(hào)或者單引號(hào)包圍。如:
body bgcolor=”red”
font size=’7’
1.4 標(biāo)記的縮進(jìn)規(guī)范
* 最高一級(jí)的父標(biāo)記采用左對(duì)齊頂格方式書寫。
* 下一級(jí)標(biāo)記采用左對(duì)齊向右縮進(jìn)一個(gè)Tab的方式書寫
在下一級(jí)依此類推,分別左對(duì)齊相對(duì)于父標(biāo)記向右縮進(jìn)一個(gè)Tab的方式書寫
* 同一級(jí)標(biāo)記的首字符上下必須對(duì)齊。
2. 客戶端JavaScript規(guī)范
2.1 變量命名規(guī)范
* 常量以及全局變量名必須全部使用大寫字母
* 變量名首字母必須小寫。
* 變量名必須使用其類型的所寫字符串開始。各種類型的所寫字符串如下:
* 整型變量:int
* 長整型變量:lng
* 浮點(diǎn)型變量:flt
* 雙精度變量:dbl
* 對(duì)象引用變量:obj
* 字符串變量:str
* Date類型變量:dtm
* 變量名必須采用有意義的單詞命名,如:
strUserName、lngArrayIndex
* 變量名除首字母小寫外,其他單詞首字符必須大寫
* 如果變量名過長可以使用單詞縮寫,除了被廣泛了解的單詞縮寫以外,所有使用單詞所寫的變量名必須在定義時(shí)給出注釋,如:
var strAdName //用于表示Administrator帳戶的名稱
var strAdminName //不用給出注釋,Admin被廣泛了解
2.2 變量使用規(guī)范
* 變量使用前必須定義。沒有定義的變量禁止使用
* 變量的使用盡量縮小到小的作用域。如循環(huán)使用
for(var I=0;I12;I++){
}
而不是:
var I;
for(I=0;I12,I++){
}
2.3 對(duì)象命名規(guī)范
各種頁面對(duì)象如text輸入框、按鈕、下拉選擇框在命名時(shí)必須使用以下對(duì)應(yīng)前綴:
* text輸入框:txt
* button按鈕:btn
* select下拉選擇框:sel
* option項(xiàng):opt
* form表單:frm
* frame框架:fra
* hidden表單項(xiàng):hdn
* div標(biāo)記:div
* span標(biāo)記:span
* 對(duì)話框?qū)ο?dlg
* 窗口對(duì)象:win
2.4 函數(shù)以及子過程命名規(guī)范
* 函數(shù)命名必須使用動(dòng)詞+名詞對(duì)的方式,并且能夠體現(xiàn)函數(shù)的功能
* 函數(shù)命名的動(dòng)詞前綴必須是同函數(shù)功能相關(guān)的完整動(dòng)詞
* 函數(shù)命名第一個(gè)單詞的首字母小寫,后面每一個(gè)單詞的首字母大寫
第三章 動(dòng)態(tài)文件編碼規(guī)范
1. HTML書寫規(guī)范
HTML書寫規(guī)范必須符合靜態(tài)文件HTML標(biāo)記書寫規(guī)范,參考(第二章第一節(jié))
2. 客戶端腳本規(guī)范
動(dòng)態(tài)文件客戶端腳本一律采用JavaScript書寫,并必須符合靜態(tài)文件編碼規(guī)范中有關(guān)JavaScript編碼規(guī)范的規(guī)定(參考第二章第二節(jié))
3. 服務(wù)器端腳本書寫規(guī)范
服務(wù)器端腳本書寫采用VBScript書寫
3.1 命名規(guī)范
3.1.1 VBScript腳本變量命名規(guī)范
* 常量以及全局變量必須全部使用大寫字母
* 常量必須使用CONST_前綴
* 全局變量必須使用G_前綴
* 變量名首字母必須小寫。
* 變量名必須使用其類型的所寫字符串開始。各種類型的所寫字符串如下:
* 整型變量:int
* 長整型變量:lng
* 浮點(diǎn)型變量:flt
* 雙精度變量:dbl
* 對(duì)象引用變量:obj
* 字符串變量:str
* Date類型變量:dtm
* 變量名必須采用有意義的單詞命名,如:
strUserName、lngArrayIndex
* 變量名除首字母小寫外,其他單詞首字符必須大寫
* 如果變量名過長可以使用單詞縮寫,除了被廣泛了解的單詞縮寫以外,所有使用單詞所寫的變量名必須在定義時(shí)給出注釋,如:
dim strAdName ‘用于表示Administrator帳戶的名稱
dim strAdminName ‘不用給出注釋,Admin被廣泛了解
3.1.2 對(duì)象命名規(guī)范
各種對(duì)象如Connection、Recordset、Command在命名時(shí)必須使用以下對(duì)應(yīng)前綴:
* Connection對(duì)象:conn
* Recordset對(duì)象:rs
* Command對(duì)象:cmd
* Parameter對(duì)象:param
* Field對(duì)象:fld
* Error對(duì)象:err
3.1.3 函數(shù)以及子過程命名規(guī)范
* 函數(shù)命名必須使用動(dòng)詞+名詞對(duì)的方式,并且能夠體現(xiàn)函數(shù)的功能
* 函數(shù)命名的動(dòng)詞前綴必須是同函數(shù)功能相關(guān)的完整動(dòng)詞
* 函數(shù)命名第一個(gè)單詞的首字母大寫,后面每一個(gè)單詞的首字母大寫
3.1.4 常用變量命名規(guī)范:
說明:包含在[]中的部分為可省略部分
* Connection對(duì)象:conn[Name]。Name為所連接數(shù)據(jù)庫的服務(wù)器名字
* Recordset變量命名規(guī)范:rs[Name]。Name為自定義的同rs存儲(chǔ)內(nèi)容有關(guān)的英文單詞組合
* Command對(duì)象:cmd[Name]。Name為自定義的同command目的有關(guān)的英文單詞組合
* SQL語句字符串變量:strSql[CommandName]。CommandName為自定義的同Sql語句功能相關(guān)的英文單詞組合,如:
strSqlUpdateModify
strSqlInsertUser
3.2 代碼書寫規(guī)范
3.2.1 變量明確聲明原則
* 所有ASP程序文件,必須在代碼的第一行包含%option explicit%。轉(zhuǎn)為變量明確聲明模式
3.2.2 字符集設(shè)定原則
* 所有將對(duì)客戶端產(chǎn)生中文輸出的ASP程序文件,必須在輸出前設(shè)定Charset為”GB2312”.如:Response.Charset = “GB2312”
3.2.3 函數(shù)使用原則
* 盡量使用函數(shù)封裝代碼塊
* 連續(xù)代碼塊盡量不要超過50行。最多不得超過70行
* 盡量使用局部變量。
* 如有涉及到全局的資源,如Connection,盡量作為函數(shù)的參數(shù)傳入
* 所有在函數(shù)內(nèi)部創(chuàng)建打開的資源,在退出函數(shù)前必須關(guān)閉釋放。如:Recordset,Command
3.2.4 Request、Session、Application使用規(guī)范
* 所有需要放入Session、Application中的對(duì)象,必須采用有意義的英文名字。除了被廣泛了解的單詞縮寫以外,不得采用單詞縮寫。如:
Session(“cp”) = strCurrentUserIP ‘不允許
Session(“CurrentUserIP”) = strCurrentUserIP
Session(“Pwd”) = strPwd ‘允許,Pwd被廣泛了解為密碼
* 所有需要在代碼內(nèi)用到的Request、Session、Application中的元素,必須在代碼頭部賦值給代碼內(nèi)聲明的變量。
* 如果獲得Form中提交的內(nèi)容,必須使用Request.Form(“itemName”).
* 如果獲得QueryString中提交的內(nèi)容,必須使用Request.QueryString(“itemName”)
* 不得在代碼中出現(xiàn)Request(“”)這樣的引用方式
3.2.5 HTML同服務(wù)器端腳本混合使用原則
* 服務(wù)器端腳本標(biāo)記“%”必須同其上一行緊鄰的標(biāo)記左對(duì)齊,如:
table
%
do while not rs.eof
%
tr
tdtext/td
/tr
%
rs.movenext
loop
%
/table
* 服務(wù)器端腳本標(biāo)記“%”同其后的代碼不得在同一行書寫
* “%”同其前面的代碼不得在同一行書寫
* 服務(wù)器端腳本標(biāo)記”%”同其最近的”%”標(biāo)記對(duì)齊
* 服務(wù)器端內(nèi)部的HTML代碼依據(jù)靜態(tài)文件的HTML縮進(jìn)規(guī)則編寫,不遵循服務(wù)器端腳本縮進(jìn)規(guī)則
* HTML標(biāo)記內(nèi)部的代碼,依據(jù)服務(wù)器端腳本的縮進(jìn)規(guī)則,不遵循HTML代碼縮進(jìn)規(guī)則 。
第四章 常見錯(cuò)誤
1. ADO的事務(wù)處理
1.1 錯(cuò)誤代碼:80004005。
1.1.1 錯(cuò)誤描述:
Microsoft OLE DB Provider for ODBC Drivers 錯(cuò)誤 ’80004005’
不能在 firehose 方式下啟動(dòng)事務(wù)
1.1.2 解決方法:
在開始ADO的事務(wù)的時(shí)候,必須首先關(guān)閉使用同一個(gè)連接對(duì)象打開的記錄集,或者在打開那些游標(biāo)集之前,設(shè)置游標(biāo)集位置類型為adUseClient.(使用客戶端游標(biāo)集)
第五章 代碼習(xí)慣書寫示例
1. ADO對(duì)象的使用
1.1 ADODB.Connection對(duì)象
1.2 ADODB.Command對(duì)象
1.3 ADODB.Recordset對(duì)象
1.3.1 創(chuàng)建:
Set rs = Server.CreateObject(“ADODB.Recordset”)
rs.CursorLocation = adUseClient
rs.Open strSql,conn,1[,1] ‘必須指定游標(biāo)類型
一、 注釋規(guī)范
A. 注釋標(biāo)準(zhǔn):
l 功能注釋
功能注釋是指為了對(duì)代碼本身進(jìn)行解釋說明而進(jìn)行的注釋。
注釋符采用“’”作為統(tǒng)一的注釋符。
1.行內(nèi)注釋
采用注釋符號(hào) “’”
例:
Dim intFileNo As Integer ’ファイル番號(hào)取得用
2.整行(包括多行)注釋
采用注釋塊開始與塊結(jié)束標(biāo)志
36
’************************************
’
’
’
’************************************
l 修訂注釋
修訂注釋是指出于測(cè)試或者改錯(cuò)等目的,對(duì)代碼進(jìn)行了更改,而必須對(duì)此修改提供相關(guān)說明和醒目標(biāo)記,并將原來的代碼加入注釋塊內(nèi)。
只要有改動(dòng),無論單行還是多行均采用設(shè)置注釋塊開始與塊結(jié)束標(biāo)志的方法來明確標(biāo)志修改部分,清楚地進(jìn)行解釋說明,便于查找和分辨注釋比較多的代碼段。
15 15
’*************** Modify Start ***************
’
’
’
’*************** End ***************
B. 需要注釋的地方:
聲明定義部分
對(duì)每個(gè)常量聲明進(jìn)行注釋;
對(duì)每個(gè)變量及類、對(duì)象等的聲明進(jìn)行注釋;
對(duì)每個(gè)自定義函數(shù)定義進(jìn)行注釋;
對(duì)每個(gè)自定義子程序定義進(jìn)行注釋;
代碼部分
對(duì)每個(gè)構(gòu)件,在頂部進(jìn)行注釋;
對(duì)每個(gè)條件選擇分支進(jìn)行注釋;
對(duì)每個(gè)詳細(xì)設(shè)計(jì)中提到的關(guān)鍵點(diǎn)進(jìn)行注釋;
對(duì)全局變量的使用進(jìn)行注釋;
C. 注釋的內(nèi)容:
l 對(duì)變量及常量聲明部分的注釋以行內(nèi)注釋方式簡要描述其用途。
l 自定義函數(shù)及子程序等定義部分的頂部進(jìn)行注釋:
’************************************
’ 概要:
’ 機(jī)能說明:
’ 參數(shù)說明:
’ 返回值:
’ 備注:
’************************************
l 代碼內(nèi)部的行內(nèi)注釋
說明具體代碼的運(yùn)算規(guī)則,循環(huán)的內(nèi)容,計(jì)數(shù)器的目的等等。
l 修訂注釋
’*************** Modify Start ***************
’ 修訂原因:
’ 修訂履歷:
’ 修訂者 修訂日期
’ 原始代碼:
’ Case 5 To 8
’ ……
Case 4 To 8
’*************** End ***************
D. 注釋的方法:
對(duì)代碼行可以在行尾加注釋(不能違反行寬的要求);
對(duì)單行代碼的注釋可以在上一行以“’”的形式添加簡單注釋;
對(duì)整段代碼的注釋放在代碼段之前;
注釋符統(tǒng)一采用“’”。
二、命名規(guī)范
A.通則
VisualBasic保留字可在VisualBasic設(shè)計(jì)器中根據(jù)顏色的變化看到。變量命名不可以使用保留字,應(yīng)使用有意義的名字命名,不可使用簡稱和無意義的名稱諸如A,x1等。即便對(duì)于只用于循環(huán)計(jì)數(shù)的變量,也應(yīng)該統(tǒng)一賦予有意義的名稱,例如longCnt等。
不能起太長的名字,應(yīng)該盡量簡潔,長度限制應(yīng)控制在32個(gè)字符之內(nèi)。
B.常數(shù)
全部使用大寫字母以表明常數(shù)意義的名詞命名,不區(qū)分常數(shù)的類型:
Const DEFAULTCONCENTRATION As Single = 0.01
C.變量
命名必須使用大小寫結(jié)合(VB編輯器會(huì)自動(dòng)轉(zhuǎn)換以減少程序出錯(cuò)的機(jī)率)
變量命名采用[范圍前綴][數(shù)組前綴][類型前綴]+[自定義命名]
控件命名采用[控件前綴]+[自定義命名]
變量范圍做前綴
范圍 前綴 例子
全局變量 g gStrUserName
模塊級(jí) m mStrUserName
過程級(jí) 無 StrUserName
數(shù)組前綴: a
類型前綴:
數(shù)據(jù)類型 前綴 例子
Boolean Bln BlnFound
Byte Byt BytRasterDate
Currency Cur CurBalance
Date Dtm DtmBeginDate
Double Dbl DblFee
Integer Int IntQty
Long Lng LngVcID
Single Sng SngAverage
String Str StrItemId
Object Obj ObjRmtsvr
ADODB.Recordset Rst RstItem
ADODB.Connection Cnn cnnNewsPaper
ADODB.Command Cmm CmmAddCustomer
Variant Vnt VntCheck
自定義類型 Udt UdtUserInfo
控件類型命名前綴
控件類型 前綴 例子
ADO Data ado AdoBiblio
Check box chk ChkReadOnly
Combo box, drop-down list box cbo CboEnglish
Command button cmd CmdExit
Common dialog dlg DlgFileOpen
Data-bound combo box dbcbo DbcboLanguage
Data-bound grid dbgrd DbgrdQueryResult
Data-bound list box dblst DblstJobType
Data combo dbc DbcAuthor
Data grid dgd DgdTitles
Data list dbl DblPublisher
Directory list box dir DirSource
Drive list box drv DrvTarget
File list box fil FilSource
Form frm FrmEntry
Frame fra FraLanguage
Graph gra GraRevenue
Grid grd GrdPrices
Horizontal scroll bar hsb HsbVolume
Image img ImgIcon
Image combo imgcbo ImgcboProduct
ImageList ils IlsAllIcons
Label lbl LblHelpMessage
Line lin LinVertical
List box lst LstPolicyCodes
ListView lvw LvwHeadings
Menu mnu MnuFileOpen
Month view mvw MvwPeriod
MS Chart ch ChSalesbyRegion
MS Flex grid msg MsgClients
MS Tab mst MstFirst
Option button opt OptGender
Picture box pic PicVGA
ProgressBar prg PrgLoadFile
Remote Data rd RdTitles
Slider sld SldScale
Spin spn SpnPages
StatusBar sta StaDateTime
SysInfo sys SysMonitor
TabStrip tab TabOptions
Text box txt TxtLastName
Timer tmr TmrAlarm
Toolbar tlb TlbActions
TreeView tre TreOrganization
UpDown upd UpdDirection
Vertical scroll bar vsb VsbRate
自行開發(fā)ActiveX控件的前綴根據(jù)具體項(xiàng)目的設(shè)計(jì)時(shí)規(guī)定。
D. 標(biāo)簽
標(biāo)簽就是用于Goto跳轉(zhuǎn)的代碼標(biāo)識(shí),由于Goto并不推薦使用,所以標(biāo)簽的使用也比較苛刻。標(biāo)簽必須全部大寫,中間的空格用下劃線_代替,而且應(yīng)該以_開頭,比如:
_A_LABEL_EXAMPLE:
如此定義標(biāo)簽是為了與其他代碼元素充分區(qū)別。
E.方法
無論是函數(shù)還是子程序,方法都必須以動(dòng)詞或動(dòng)詞短語命名。無需區(qū)分函數(shù)和子程序,也無需指明返回類型。
Sub Open(ByVal StrCommandString As String)
Function SetCopyNumber(ByVal IntCopyNumber As Integer) as Integer
參數(shù)需要指明ByVal還是ByRef,這一點(diǎn)寫起來會(huì)讓程序變長,但非常必要。如果沒有特別情況,都使用ByVal。參數(shù)的命名方法,參考 “變量的命名方法”。
三、 書寫格式規(guī)范
A. 程序的書寫順序
該構(gòu)件的概要注釋說明
變量聲明
過程聲明
代碼段1
代碼段2
……
B. 大小寫
變量名范圍前綴用小寫,每個(gè)單詞第一個(gè)字母用大寫
函數(shù)、過程、對(duì)象名也要求每個(gè)組成單詞字首大寫
C. 縮進(jìn)
統(tǒng)一開發(fā)環(huán)境,設(shè)定VisualBasic設(shè)計(jì)器的開發(fā)環(huán)境選項(xiàng),定義Tab寬度為4。代碼縮進(jìn)時(shí),先選中要縮進(jìn)的代碼塊,然后使用快捷鍵是Tab(右移)和Shift+Tab(左移);如果手工輸入空格完成縮進(jìn),以4個(gè)空格為單位。
在If語句后縮進(jìn);
在Else語句后縮進(jìn)
在Select Case語句后縮進(jìn)
在Case語句后縮進(jìn)
在Do語句后縮進(jìn)
在For語句后縮進(jìn)
已經(jīng)用行接續(xù)符分割的語句的各個(gè)行要縮進(jìn)
在With語句后縮進(jìn)。
對(duì)從屬于行標(biāo)注的代碼進(jìn)行縮進(jìn)。
D. 空格
運(yùn)算符前后都要空格,包括:+,-,*,/,^,=,,=,,=,,NOT,AND,OR等;
E. 空行
變量聲明部分和代碼語句間的分隔;
在執(zhí)行統(tǒng)一任務(wù)的各個(gè)語句組之間插入一個(gè)空行。好的代碼應(yīng)由按邏輯順序排列的進(jìn)程或相關(guān)語句組構(gòu)成。
F. 頁寬
對(duì)較長語句,如API聲明等,在代碼窗體可視范圍內(nèi)給予換行,不要使別人必須通過滾動(dòng)窗口才能查看到完整的代碼,單行代碼長度不超過95列。
使用“ _ ”換行符。
G. 其他
在項(xiàng)目組內(nèi)部,根據(jù)需要統(tǒng)一VisualBasic開發(fā)環(huán)境參數(shù)。
四、 代碼檢查
代碼檢查的合格標(biāo)準(zhǔn)
注釋完整、命名規(guī)范、條理清晰、可讀性強(qiáng)的代碼視為合格代碼。
檢查辦法
發(fā)現(xiàn)未遵循本編碼規(guī)范的情況視為不合格;
五、 建議性規(guī)范
l 有的時(shí)候可能需要違背好的編程原則,或者使用了某些不正規(guī)的方法,遇到這種情況時(shí),必須用詳細(xì)的注釋來說明在做什么和為什么要這樣做。
技巧性特別高的代碼段,一定要加詳細(xì)的注釋,不要讓其他開發(fā)人員花很長時(shí)間來研究一個(gè)高技巧但不易理解的程序段。
l 對(duì)注釋進(jìn)行縮進(jìn),使之與后隨的語句對(duì)齊。
注釋通常位于它們要說明的代碼的前面。為了從視覺上突出注釋與它的代碼之間的關(guān)系,請(qǐng)將注釋縮進(jìn),使之與代碼處于同一個(gè)層次上
六、 其他
對(duì)文檔的理解產(chǎn)生的歧義由引用此文檔的項(xiàng)目的項(xiàng)目負(fù)責(zé)人統(tǒng)一解釋。
相信對(duì)你有所幫助
此篇文章流傳甚廣, 其實(shí)里面沒啥干貨, 而且里面很多觀點(diǎn)是有問題的. 這個(gè)文章在 golang-china 很早就討論過了.
最近因?yàn)?Rust 1.0 和 1.1 的發(fā)布, 導(dǎo)致這個(gè)文章又出來毒害讀者.
所以寫了這篇反駁文章, 指出其中的問題.
有好幾次,當(dāng)我想起來的時(shí)候,總是會(huì)問自己:我為什么要放棄Go語言?這個(gè)決定是正確的嗎?是明智和理性的嗎?其實(shí)我一直在認(rèn)真思考這個(gè)問題。
開門見山地說,我當(dāng)初放棄Go語言(golang),就是因?yàn)閮蓚€(gè)“不爽”:第一,對(duì)Go語言本身不爽;第二,對(duì)Go語言社區(qū)里的某些人不爽。毫無疑問,這是非常主觀的結(jié)論。但是我有足夠詳實(shí)的客觀的論據(jù),用以支撐這個(gè)看似主觀的結(jié)論。
文末附有本文更新日志。
確實(shí)是非常主觀的結(jié)論, 因?yàn)槔锩嬗胁簧儆袉栴}的觀點(diǎn)(用來忽悠Go小白還行).
第0節(jié):我的Go語言經(jīng)歷
先說說我的經(jīng)歷吧,以避免被無緣無故地當(dāng)作Go語言的低級(jí)黑。
2009年底,Go語言(golang)第一個(gè)公開版本發(fā)布,籠罩著“Google公司制造”的光環(huán),吸引了許多慕名而來的嘗鮮者,我(Liigo)也身居其中,籠統(tǒng)的看了一些Go語言的資料,學(xué)習(xí)了基礎(chǔ)的教程,因?qū)ζ湔Z法中的分號(hào)和花括號(hào)不滿,很快就遺忘掉了,沒拿它當(dāng)一回事。
在2009年Go剛發(fā)布時(shí), 確實(shí)是因?yàn)椤癎oogle公司制造”的光環(huán)而吸引了(包括文章作者和諸多IT記者)很多低級(jí)的嘗鮮者.
還好, 經(jīng)過5年的發(fā)展, 這些純粹因?yàn)楣猸h(huán)來的投機(jī)者所剩已經(jīng)不多了(Google趨勢(shì)).
目前, 真正的Go用戶早就將Go用于實(shí)際的生產(chǎn)了.
說到 其語法中的分號(hào)和花括號(hào)不滿, 我想說這只是你的 個(gè)人主觀感受, 還有很多人對(duì)Go的分號(hào)和花括號(hào)很滿意,
包括水果公司的的 Swift 的語言設(shè)計(jì)者也很滿意這種風(fēng)格(Swift中的分號(hào)和花括號(hào)和Go基本相同).
如果只談 個(gè)人主觀感受, 我也可以說 Rust 的 fn 縮寫也很蛋疼!
兩年之后,2011年底,Go語言發(fā)布1.0的計(jì)劃被提上日程,相關(guān)的報(bào)道又多起來,我再次關(guān)注它,重新評(píng)估之后決定深入?yún)⑴cGo語言。我訂閱了其users、nuts、dev、commits等官方郵件組,堅(jiān)持每天閱讀其中的電子郵件,以及開發(fā)者提交的每一次源代碼更新,給Go提交了許多改進(jìn)意見,甚至包括修改Go語言編譯器源代碼直接參與開發(fā)任務(wù)。如此持續(xù)了數(shù)月時(shí)間。
這個(gè)到是事實(shí), 在 golang-china 有不少吵架的帖子, 感興趣的可以去挖下, 我就不展開說了.
到2012年初,Go 1.0發(fā)布,語言和標(biāo)準(zhǔn)庫都已經(jīng)基本定型,不可能再有大幅改進(jìn),我對(duì)Go語言未能在1.0定型之前更上一個(gè)臺(tái)階、實(shí)現(xiàn)自我突破,甚至帶著諸多明顯缺陷走向1.0,感到非常失望,因而逐漸疏遠(yuǎn)了它(所以Go 1.0之后的事情我很少關(guān)心)。后來看到即將發(fā)布的Go 1.1的Release Note,發(fā)現(xiàn)語言層面沒有太大改變,只是在庫和工具層面有所修補(bǔ)和改進(jìn),感到它尚在幼年就失去成長的動(dòng)力,越發(fā)失望。外加Go語言社區(qū)里的某些人,其中也包括Google公司負(fù)責(zé)開發(fā)Go語言的某些人,其態(tài)度、言行,讓我極度厭惡,促使我決絕地離棄Go語言。
真的不清楚樓主說的可以在 Go1.0 之前短時(shí)間內(nèi)能實(shí)現(xiàn)的 重大改進(jìn)和諸多明顯缺陷 是什么.
如果是樓主說前面的 其語法中的分號(hào)和花括號(hào)不滿 之類的重大改進(jìn), 我只能說這只是你的 個(gè)人主觀感受 而已,
你的很多想法只能說服你自己, 沒辦法說服其他絕大部分人(不要以為像C++或Rust那樣什么特性都有就NB了, 各種NB特性加到一起只能是 要你命3000, 而絕對(duì)不會(huì)是什么 銀彈).
Go 1.1的Release Note,發(fā)現(xiàn)語言層面沒有太大改變. 語言層沒有改變是是因?yàn)?Go1 作出的向后兼容的承諾. 對(duì)于工業(yè)級(jí)的語言來說, Go1 這個(gè)只能是優(yōu)點(diǎn). 如果連語言層在每個(gè)版本都會(huì)出現(xiàn)諸多大幅改進(jìn), 那誰還敢用Go語言來做生產(chǎn)開發(fā)呢(我承認(rèn)Rust的改動(dòng)很大膽, 但也說明了Rust還處于比較幼稚和任性的階段)?
說 Go語言社區(qū)里的某些人固執(zhí) 的觀點(diǎn)我是同意的. 但是這些 固執(zhí) 的人是可以講道理的, 但是他們對(duì)很多東西的要求很高(特別是關(guān)于Go的設(shè)計(jì)哲學(xué)部分).
只要你給的建議有依據(jù)(語言的設(shè)計(jì)哲學(xué)是另外一回事情), 他們絕對(duì)不會(huì)盲目的拒絕(只是討論的周期會(huì)比較長).
關(guān)于樓主提交的給Go文件添加BOM的文章, 需要補(bǔ)充說明下.
在Go1.0發(fā)布的時(shí)候, Go語言的源文件(.go)明確要求必須是UTF8編碼的, 而且是無BOM的UTF8編碼的.
注意: 這個(gè) 無BOM的UTF8編碼 的限制僅僅是 針對(duì) Go語言的源文件(.go).
這個(gè)限制并不是說不允許用戶處理帶BOM的UTF8的txt文件!
我覺得對(duì)于寫Go程序來說, 這個(gè)限制是沒有任何問題的, 到目前為止, 我還從來沒有使用過帶BOM的.go文件.
不僅是因?yàn)閹OM的.go文件沒有太多的意義, 而且有很多的缺陷.
BOM的原意是用來表示編碼是大端還是小端的, 主要用于UTF16和UTF32. 對(duì)于 UTF8 來說, BOM 沒有任何存在的意義(正是Go的2個(gè)作者發(fā)明了UTF8, 徹底解決了全球的編碼問題).
但是, 在現(xiàn)實(shí)中, 因?yàn)镸S的txt記事本, 對(duì)于中文環(huán)境會(huì)將txt(甚至是C/C++源文件)當(dāng)作GBK編碼(GBK是個(gè)爛編碼),
為了區(qū)別到底是GBK還是UTF8, MS的記事本在前面加了BOM這個(gè)垃圾(被GBK占了茅坑), 這里的bom已經(jīng)不是表示字節(jié)序本意了. 不知道有沒有人用ms的記事本寫網(wǎng)頁, 然后生成一個(gè)帶bom的utf8網(wǎng)頁肯定很有意思.
這是MS的記事本的BUG: 它不支持生成無BOM的UTF8編碼的文本文件!
這些是現(xiàn)實(shí)存在的帶BOM的UTF8編碼的文本文件, 但是它們肯定都不是Go語言源文件!
所以說, Go語言的源文件即使強(qiáng)制限制了無BOM的UTF8編碼要求, 也是沒有任何問題的(而且我還希望有這個(gè)限制).
雖然后來Go源文件接受帶BOM的UTF8了, 但是運(yùn)行 go fmt 之后, 還是會(huì)刪除掉BOM的(因?yàn)锽OM就是然并卵). 也就是說 帶 BOM 的 Go 源文件是不符合 Go語言的編碼風(fēng)格的, go fmt 會(huì)強(qiáng)制刪除 BOM 頭.
前面說了BOM是MS帶來的垃圾, 但是BOM的UTF8除了然并卵之外還有很多問題, 因?yàn)锽OM在string的開頭嵌入了垃圾,
導(dǎo)致正則表達(dá)式, string的鏈接運(yùn)算等操作都被會(huì)被BOM這個(gè)垃圾所污染. 對(duì)于.go語言, 即使代碼完全一樣, 有BOM和無BOM會(huì)導(dǎo)致文件的MD5之類的校驗(yàn)碼不同.
所以, 我覺得Go用戶不用糾結(jié)BOM這個(gè)無關(guān)緊要的東西.
在上一個(gè)10年,我(Liigo)在我所屬的公司里,深度參與了兩個(gè)編程語言項(xiàng)目的開發(fā)。我想,對(duì)于如何判斷某個(gè)編程語言的優(yōu)劣,或者說至少對(duì)于如何判斷某個(gè)編程語言是否適合于我自己,我應(yīng)該還是有一點(diǎn)發(fā)言權(quán)的。
第1節(jié):我為什么對(duì)Go語言不爽?
Go語言有很多讓我不爽之處,這里列出我現(xiàn)在還能記起的其中一部分,排名基本上不分先后。讀者們耐心地看完之后,還能淡定地說一句“我不在乎”嗎?
1.1 不允許左花括號(hào)另起一行
關(guān)于對(duì)花括號(hào)的擺放,在C語言、C++、Java、C#等社區(qū)中,十余年來存在持續(xù)爭議,從未形成一致意見。在我看來,這本來就是主觀傾向很重的抉擇,不違反原則不涉及是非的情況下,不應(yīng)該搞一刀切,讓程序員或團(tuán)隊(duì)自己選擇就足夠了。編程語言本身強(qiáng)行限制,把自己的喜好強(qiáng)加給別人,得不償失。無論傾向于其中任意一種,必然得罪與其對(duì)立的一群人。雖然我現(xiàn)在已經(jīng)習(xí)慣了把左花括號(hào)放在行尾,但一想到被禁止其他選擇,就感到十分不爽。Go語言這這個(gè)問題上,沒有做到“團(tuán)結(jié)一切可以團(tuán)結(jié)的力量”不說,還有意給自己樹敵,太失敗了。
我覺得Go最偉大的發(fā)明是 go fmt, 從此Go用戶不會(huì)再有花括弧的位置這種無聊爭論了(當(dāng)然也少了不少灌水和上tiobe排名的機(jī)會(huì)).
是這優(yōu)點(diǎn), Swift 語言也使用和 Go 類似的風(fēng)格(當(dāng)然樓主也可能鄙視swift的作者).
1.2 編譯器莫名其妙地給行尾加上分號(hào)
對(duì)Go語言本身而言,行尾的分號(hào)是可以省略的。但是在其編譯器(gc)的實(shí)現(xiàn)中,為了方便編譯器開發(fā)者,卻在詞法分析階段強(qiáng)行添加了行尾的分號(hào),反過來又影響到語言規(guī)范,對(duì)“怎樣添加分號(hào)”做出特殊規(guī)定。這種變態(tài)做法前無古人。在左花括號(hào)被意外放到下一行行首的情況下,它自動(dòng)在上一行行尾添加的分號(hào),會(huì)導(dǎo)致莫名其妙的編譯錯(cuò)誤(Go 1.0之前),連它自己都解釋不明白。如果實(shí)在處理不好分號(hào),干脆不要省略分號(hào)得了;或者,Scala和JavaScript的編譯器是開源的,跟它們學(xué)學(xué)怎么處理省略行尾分號(hào)可以嗎?
又是樓主的 個(gè)人主觀感受, 不過我很喜歡這個(gè)特性. Swift 語言也是類似.
1.3 極度強(qiáng)調(diào)編譯速度,不惜放棄本應(yīng)提供的功能
程序員是人不是神,編碼過程中免不了因?yàn)榇笠饣蚴韬龇敢恍╁e(cuò)。其中有一些,是大家集體性的很容易就中招的錯(cuò)誤(Go語言里的例子我暫時(shí)想不起來,C++里的例子有“基類析構(gòu)函數(shù)不是虛函數(shù)”)。這時(shí)候編譯器應(yīng)該站出來,多做一些檢查、約束、核對(duì)性工作,盡量阻止常規(guī)錯(cuò)誤的發(fā)生,盡量不讓有潛在錯(cuò)誤的代碼編譯通過,必要時(shí)給出一些警告或提示,讓程序員留意。編譯器不就是機(jī)器么,不就是應(yīng)該多做臟活累活雜活、減少人的心智負(fù)擔(dān)么?編譯器多做一項(xiàng)檢查,可能會(huì)避免數(shù)十萬程序員今后多年內(nèi)無數(shù)次犯同樣的錯(cuò)誤,節(jié)省的時(shí)間不計(jì)其數(shù),這是功德無量的好事。但是Go編譯器的作者們可不這么想,他們不愿意自己多花幾個(gè)小時(shí)給編譯器增加新功能,覺得那是虧本,反而減慢了編譯速度。他們以影響編譯速度為由,拒絕了很多對(duì)編譯器改進(jìn)的要求。典型的因噎廢食。強(qiáng)調(diào)編譯速度固然值得贊賞,但如果因此放棄應(yīng)有的功能,我不贊成。
編譯速度是很重要的, 如果編譯速度夠慢, 語言再好也不會(huì)有人使用的.
比如C/C++的增量編譯/預(yù)編譯頭文件/并發(fā)編譯都是為了提高編譯速度.
Rust1.1 也號(hào)稱 比 1.0 的編譯時(shí)間減少了32% (注意: 不是運(yùn)行速度).
當(dāng)然, Go剛面世的時(shí)候, 編譯速度是其中的一個(gè)設(shè)計(jì)目標(biāo).
不過我想樓主, 可能想說的是因?yàn)榫幾g器自己添加分號(hào)而導(dǎo)致的編譯錯(cuò)誤的問題.
我覺得Go中 { 不能另起一行是語言特性, 如果修復(fù)這個(gè)就是引入了新的錯(cuò)誤.
其他的我真想不起來還有哪些 調(diào)編譯速度,不惜放棄本應(yīng)提供的功能 (不要提泛型, 那是因?yàn)檫€沒有好的設(shè)計(jì)).
1.4 錯(cuò)誤處理機(jī)制太原始
在Go語言中處理錯(cuò)誤的基本模式是:函數(shù)通常返回多個(gè)值,其中最后一個(gè)值是error類型,用于表示錯(cuò)誤類型極其描述;調(diào)用者每次調(diào)用完一個(gè)函數(shù),都需要檢查這個(gè)error并進(jìn)行相應(yīng)的錯(cuò)誤處理:if err != nil { /*這種代碼寫多了不想吐么*/ }。此模式跟C語言那種很原始的錯(cuò)誤處理相比如出一轍,并無實(shí)質(zhì)性改進(jìn)。實(shí)際應(yīng)用中很容易形成多層嵌套的if else語句,可以想一想這個(gè)編碼場(chǎng)景:先判斷文件是否存在,如果存在則打開文件,如果打開成功則讀取文件,如果讀取成功再寫入一段數(shù)據(jù),最后關(guān)閉文件,別忘了還要處理每一步驟中出現(xiàn)錯(cuò)誤的情況,這代碼寫出來得有多變態(tài)、多丑陋?實(shí)踐中普遍的做法是,判斷操作出錯(cuò)后提前return,以避免多層花括號(hào)嵌套,但這么做的后果是,許多錯(cuò)誤處理代碼被放在前面突出的位置,常規(guī)的處理邏輯反而被掩埋到后面去了,代碼可讀性極差。而且,error對(duì)象的標(biāo)準(zhǔn)接口只能返回一個(gè)錯(cuò)誤文本,有時(shí)候調(diào)用者為了區(qū)分不同的錯(cuò)誤類型,甚至需要解析該文本。除此之外,你只能手工強(qiáng)制轉(zhuǎn)換error類型到特定子類型(靜態(tài)類型的優(yōu)勢(shì)沒了)。至于panic - recover機(jī)制,致命的缺陷是不能跨越庫的邊界使用,注定是一個(gè)半成品,最多只能在自己的pkg里面玩一玩。Java的異常處理雖然也有自身的問題(比如Checked Exceptions),但總體上還是比Go的錯(cuò)誤處理高明很多。
話說, 軟件開發(fā)都發(fā)展了半個(gè)世紀(jì), 還是無實(shí)質(zhì)性改進(jìn). 不要以為弄一個(gè)異常的語法糖就是革命了.
我只能說錯(cuò)誤和異常是2個(gè)不同的東西, 將所有錯(cuò)誤當(dāng)作異常那是SB行為.
正因?yàn)橛挟惓_@個(gè)所謂的銀彈, 導(dǎo)致很多等著別人幫忙擦屁股的行為(注意 shit 函數(shù)拋出的絕對(duì)不會(huì)是一種類型的 shit, 而被其間接調(diào)用的各種 xxx_shit 也可能拋出各種類型的異常, 這就導(dǎo)致 catch 失控了):
int main() {
try {
shit();
} catch( /* 到底有幾千種 shit ? */) {
...
}
}
Go的建議是 panic - recover 不跨越邊界, 也就是要求正常的錯(cuò)誤要由pkg的處理掉.
這是負(fù)責(zé)任的行為.
再說Go是面向并發(fā)的編程語言, 在海量的 goroutine 中使用 try/catch 是不是有一種不倫不類的感覺呢?
1.5 垃圾回收器(GC)不完善、有重大缺陷
在Go 1.0前夕,其垃圾回收器在32位環(huán)境下有內(nèi)存泄漏,一直拖著不肯改進(jìn),這且不說。Go語言垃圾回收器真正致命的缺陷是,會(huì)導(dǎo)致整個(gè)進(jìn)程不可預(yù)知的間歇性停頓。像某些大型后臺(tái)服務(wù)程序,如游戲服務(wù)器、APP容器等,由于占用內(nèi)存巨大,其內(nèi)存對(duì)象數(shù)量極多,GC完成一次回收周期,可能需要數(shù)秒甚至更長時(shí)間,這段時(shí)間內(nèi),整個(gè)服務(wù)進(jìn)程是阻塞的、停頓的,在外界看來就是服務(wù)中斷、無響應(yīng),再牛逼的并發(fā)機(jī)制到了這里統(tǒng)統(tǒng)失效。垃圾回收器定期啟動(dòng),每次啟動(dòng)就導(dǎo)致短暫的服務(wù)中斷,這樣下去,還有人敢用嗎?這可是后臺(tái)服務(wù)器進(jìn)程,是Go語言的重點(diǎn)應(yīng)用領(lǐng)域。以上現(xiàn)象可不是我假設(shè)出來的,而是事實(shí)存在的現(xiàn)實(shí)問題,受其嚴(yán)重困擾的也不是一家兩家了(2013年底ECUG Con 2013,京東的劉奇提到了Go語言的GC、defer、標(biāo)準(zhǔn)庫實(shí)現(xiàn)是性能殺手,最大的痛苦是GC;美團(tuán)的沈鋒也提到Go語言的GC導(dǎo)致后臺(tái)服務(wù)間隔性停頓是最大的問題。更早的網(wǎng)絡(luò)游戲仙俠道開發(fā)團(tuán)隊(duì)也曾受Go垃圾回收的沉重打擊)。在實(shí)踐中,你必須努力減少進(jìn)程中的對(duì)象數(shù)量,以便把GC導(dǎo)致的間歇性停頓控制在可接受范圍內(nèi)。除此之外你別無選擇(難道你還想自己更換GC算法、甚至砍掉GC?那還是Go語言嗎?)。跳出圈外,我近期一直在思考,一定需要垃圾回收器嗎?沒有垃圾回收器就一定是歷史的倒退嗎?(可能會(huì)新寫一篇博客文章專題探討。)
這是說的是32位系統(tǒng), 這絕對(duì)不是Go語言的重點(diǎn)應(yīng)用領(lǐng)域!! 我可以說Go出生就是面向64位系統(tǒng)和多核心CPU環(huán)境設(shè)計(jì)的. (再說 Rust 目前好像還不支持 XP 吧, 這可不可以算是影響巨大?)
32位當(dāng)時(shí)是有問題, 但是對(duì)實(shí)際生產(chǎn)影響并不大(請(qǐng)問樓主還是在用32位系統(tǒng)嗎, 還只安裝4GB的內(nèi)存嗎). 如果是8位單片機(jī)環(huán)境, 建議就不要用Go語言了, 直接C語言好了.
而且這個(gè)問題早就不存在了(大家可以去看Go的發(fā)布日志).
Go的出生也就5年時(shí)間, GC的完善和改進(jìn)是一個(gè)持續(xù)的工作, 2015年8月將發(fā)布的 Go1.5將采用并行GC.
關(guān)于GC的被人詬病的地方是會(huì)導(dǎo)致卡頓, 但是我以為這個(gè)主要是因?yàn)镚C的實(shí)現(xiàn)還不夠完美而導(dǎo)致的.
如果是完美的并發(fā)和增量的GC, 那應(yīng)該不會(huì)出現(xiàn)大的卡頓問題的.
當(dāng)然, 如果非要實(shí)時(shí)性, 那用C好了(實(shí)時(shí)并不表示性能高, 只是響應(yīng)時(shí)間可控).
對(duì)于Rust之類沒有GC的語言來說, 想很方便的開發(fā)并發(fā)的后臺(tái)程序那幾乎是不可能的.
不要總是吹Rust能代替底層/中層/上層的開發(fā), 我們要看有誰用Rust真的做了什么.
1.6 禁止未使用變量和多余import
Go編譯器不允許存在被未被使用的變量和多余的import,如果存在,必然導(dǎo)致編譯錯(cuò)誤。但是現(xiàn)實(shí)情況是,在代碼編寫、重構(gòu)、調(diào)試過程中,例如,臨時(shí)性的注釋掉一行代碼,很容易就會(huì)導(dǎo)致同時(shí)出現(xiàn)未使用的變量和多余的import,直接編譯錯(cuò)誤了,你必須相應(yīng)的把變量定義注釋掉,再翻頁回到文件首部把多余的import也注釋掉,……等事情辦完了,想把剛才注釋的代碼找回來,又要好幾個(gè)麻煩的步驟。還有一個(gè)讓人蛋疼的問題,編寫數(shù)據(jù)庫相關(guān)的代碼時(shí),如果你import某數(shù)據(jù)庫驅(qū)動(dòng)的pkg,它編譯給你報(bào)錯(cuò),說不需要import這個(gè)未被使用的pkg;但如果你聽信編譯器的話刪掉該import,編譯是通過了,運(yùn)行時(shí)必然報(bào)錯(cuò),說找不到數(shù)據(jù)庫驅(qū)動(dòng);你看看程序員被折騰的兩邊不是人,最后不得不請(qǐng)出大神:import _。對(duì)待這種問題,一個(gè)比較好的解決方案是,視其為編譯警告而非編譯錯(cuò)誤。但是Go語言開發(fā)者很固執(zhí),不容許這種折中方案。
這個(gè)問題我只能說樓主的吐槽真的是沒水平.
為何不使用的是錯(cuò)誤而不是警告? 這是為了將低級(jí)的bug消滅在編譯階段(大家可以想下C/C++的那么多警告有什么卵用).
而且, import 即使沒有使用的話, 也是用副作用的, 因?yàn)?import 會(huì)導(dǎo)致 init 和全局變量的初始化.
如果某些代碼沒有使用, 為何要執(zhí)行 init 這些初始化呢?
如果是因?yàn)檎{(diào)試而添加的變量, 那么調(diào)試完刪除不是很正常的要求嗎?
如果是因?yàn)檎{(diào)試而要導(dǎo)入fmt或log之類的包, 刪除調(diào)試代碼后又導(dǎo)致 import 錯(cuò)誤的花,
樓主難道不知道在一個(gè)獨(dú)立的文件包裝下類似的輔助調(diào)試的函數(shù)嗎?
import (
"fmt"
"log"
)
func logf(format string, a ...interface{}) {
file, line := callerFileLine()
fmt.Fprintf(os.Stderr, "%s:%d: ", file, line)
fmt.Fprintf(os.Stderr, format, a...)
}
func fatalf(format string, a ...interface{}) {
file, line := callerFileLine()
fmt.Fprintf(os.Stderr, "%s:%d: ", file, line)
fmt.Fprintf(os.Stderr, format, a...)
os.Exit(1)
}
import _ 是有明確行為的用法, 就是為了執(zhí)行包中的 init 等函數(shù)(可以做某些注冊(cè)操作).
將警告當(dāng)作錯(cuò)誤是Go的一個(gè)哲學(xué), 當(dāng)然在樓主看來這是白癡做法.
1.7 創(chuàng)建對(duì)象的方式太多令人糾結(jié)
創(chuàng)建對(duì)象的方式,調(diào)用new函數(shù)、調(diào)用make函數(shù)、調(diào)用New方法、使用花括號(hào)語法直接初始化結(jié)構(gòu)體,你選哪一種?不好選擇,因?yàn)闆]有一個(gè)固定的模式。從實(shí)踐中看,如果要?jiǎng)?chuàng)建一個(gè)語言內(nèi)置類型(如channel、map)的對(duì)象,通常用make函數(shù)創(chuàng)建;如果要?jiǎng)?chuàng)建標(biāo)準(zhǔn)庫或第三方庫定義的類型的對(duì)象,首先要去文檔里找一下有沒有New方法,如果有就最好調(diào)用New方法創(chuàng)建對(duì)象,如果沒有New方法,則退而求其次,用初始化結(jié)構(gòu)體的方式創(chuàng)建其對(duì)象。這個(gè)過程頗為周折,不像C++、Java、C#那樣直接new就行了。
C++的new是狗屎. new導(dǎo)致的問題是構(gòu)造函數(shù)和普通函數(shù)的行為不一致, 這個(gè)補(bǔ)丁特性真的沒啥優(yōu)越的.
我還是喜歡C語言的 fopen 和 malloc 之類構(gòu)造函數(shù), 構(gòu)造函數(shù)就是普通函數(shù), Go語言中也是這樣.
C++中, 除了構(gòu)造不兼容普通函數(shù), 析構(gòu)函數(shù)也是不兼容普通函數(shù). 這個(gè)而引入的坑有很多吧.
1.8 對(duì)象沒有構(gòu)造函數(shù)和析構(gòu)函數(shù)
沒有構(gòu)造函數(shù)還好說,畢竟還有自定義的New方法,大致也算是構(gòu)造函數(shù)了。沒有析構(gòu)函數(shù)就比較難受了,沒法實(shí)現(xiàn)RAII。額外的人工處理資源清理工作,無疑加重了程序員的心智負(fù)擔(dān)。沒人性啊,還嫌我們程序員加班還少嗎?C++里有析構(gòu)函數(shù),Java里雖然沒有析構(gòu)函數(shù)但是有人家finally語句啊,Go呢,什么都沒有。沒錯(cuò),你有個(gè)defer,可是那個(gè)defer問題更大,詳見下文吧。
defer 可以覆蓋析構(gòu)函數(shù)的行為, 當(dāng)然 defer 還有其他的任務(wù). Swift2.0 也引入了一個(gè)簡化版的 defer 特性.
1.9 defer語句的語義設(shè)定不甚合理
Go語言設(shè)計(jì)defer語句的出發(fā)點(diǎn)是好的,把釋放資源的“代碼”放在靠近創(chuàng)建資源的地方,但把釋放資源的“動(dòng)作”推遲(defer)到函數(shù)返回前執(zhí)行。遺憾的是其執(zhí)行時(shí)機(jī)的設(shè)置似乎有些不甚合理。設(shè)想有一個(gè)需要長期運(yùn)行的函數(shù),其中有無限循環(huán)語句,在循環(huán)體內(nèi)不斷的創(chuàng)建資源(或分配內(nèi)存),并用defer語句確保釋放。由于函數(shù)一直運(yùn)行沒有返回,所有defer語句都得不到執(zhí)行,循環(huán)過程中創(chuàng)建的大量短暫性資源一直積累著,得不到回收。而且,系統(tǒng)為了存儲(chǔ)defer列表還要額外占用資源,也是持續(xù)增加的。這樣下去,過不了多久,整個(gè)系統(tǒng)就要因?yàn)橘Y源耗盡而崩潰。像這類長期運(yùn)行的函數(shù),http.ListenAndServe()就是典型的例子。在Go語言重點(diǎn)應(yīng)用領(lǐng)域,可以說幾乎每一個(gè)后臺(tái)服務(wù)程序都必然有這么一類函數(shù),往往還都是程序的核心部分。如果程序員不小心在這些函數(shù)中使用了defer語句,可以說后患無窮。如果語言設(shè)計(jì)者把defer的語義設(shè)定為在所屬代碼塊結(jié)束時(shí)(而非函數(shù)返回時(shí))執(zhí)行,是不是更好一點(diǎn)呢?可是Go 1.0早已發(fā)布定型,為了保持向后兼容性,已經(jīng)不可能改變了。小心使用defer語句!一不小心就中招。
前面說到 defer 還有其他的任務(wù), 也就是 defer 中執(zhí)行的 recover 可以捕獲 panic 拋出的異常.
還有 defer 可以在 return 之后修改命名的返回值.
上面2個(gè)工作要求 defer 只能在函數(shù)退出時(shí)來執(zhí)行.
樓主說的 defer 是類似 Swift2.0 中 defer 的行為, 但是 Swift2.0 中 defer 是沒有前面2個(gè)特性的.
Go中的defer是以函數(shù)作用域作為觸發(fā)的條件的, 是會(huì)導(dǎo)致樓主說的在 for 中執(zhí)行的錯(cuò)誤用法(哪個(gè)語言沒有坑呢?).
不過 for 中 局部 defer 也是有辦法的 (Go中的defer是以函數(shù)作用域):
for {
func(){
f, err := os.Open(...)
defer f.Close()
}()
}
在 for 中做一個(gè)閉包函數(shù)就可以了. 自己不會(huì)用不要怪別人沒告訴你.
1.10 許多語言內(nèi)置設(shè)施不支持用戶定義的類型
for in、make、range、channel、map等都僅支持語言內(nèi)置類型,不支持用戶定義的類型(?)。用戶定義的類型沒法支持for in循環(huán),用戶不能編寫像make、range那樣“參數(shù)類型和個(gè)數(shù)”甚至“返回值類型和個(gè)數(shù)”都可變的函數(shù),不能編寫像channel、map那樣類似泛型的數(shù)據(jù)類型。語言內(nèi)置的那些東西,處處充斥著斧鑿的痕跡。這體現(xiàn)了語言設(shè)計(jì)的局限性、封閉性、不完善,可擴(kuò)展性差,像是新手作品——且不論其設(shè)計(jì)者和實(shí)現(xiàn)者如何權(quán)威。延伸閱讀:Go語言是30年前的陳舊設(shè)計(jì)思想,用戶定義的東西幾乎都是二等公民(Tikhon Jelvis)。
說到底, 這個(gè)是因?yàn)閷?duì)泛型支持的不完備導(dǎo)致的.
Go語言是沒啥NB的特性, 但是Go的特性和工具組合在一起就是好用.
這就是Go語言NB的地方.
1.11 沒有泛型支持,常見數(shù)據(jù)類型接口丑陋
沒有泛型的話,List、Set、Tree這些常見的基礎(chǔ)性數(shù)據(jù)類型的接口就只能很丑陋:放進(jìn)去的對(duì)象是一個(gè)具體的類型,取出來之后成了無類型的interface{}(可以視為所有類型的基礎(chǔ)類型),還得強(qiáng)制類型轉(zhuǎn)換之后才能繼續(xù)使用,令人無語。Go語言缺少min、max這類函數(shù),求數(shù)值絕對(duì)值的函數(shù)abs只接收/返回雙精度小數(shù)類型,排序接口只能借助sort.Interface無奈的回避了被比較對(duì)象的類型,等等等等,都是沒有泛型導(dǎo)致的結(jié)果。沒有泛型,接口很難優(yōu)雅起來。Go開發(fā)者沒有明確拒絕泛型,只是說還沒有找到很好的方法實(shí)現(xiàn)泛型(能不能學(xué)學(xué)已經(jīng)開源的語言呀)?,F(xiàn)實(shí)是,Go 1.0已經(jīng)定型,泛型還沒有,那些丑陋的接口為了保持向后兼容必須長期存在著。
Go有自己的哲學(xué), 如果能有和目前哲學(xué)不沖突的泛型實(shí)現(xiàn), 他們是不會(huì)反對(duì)的.
如果只是簡單學(xué)學(xué)(或者叫抄襲)已經(jīng)開源的語言的語法, 那是C++的設(shè)計(jì)風(fēng)格(或者說C++從來都是這樣設(shè)計(jì)的, 有什么特性就抄什么), 導(dǎo)致了各種腦裂的編程風(fēng)格.
編譯時(shí)泛型和運(yùn)行時(shí)泛型可能是無法完全兼容的, 看這個(gè)例子:
type AdderT interface {
Add(a, b T) T
}
沒想到這年代還有人在學(xué)習(xí)Director,已經(jīng)比較少有人知道Lingo了。全局變量放在幀面板最下部有個(gè)專門放代碼的地方,左側(cè)圖標(biāo)能看出來,這一點(diǎn)與flash相比是不一樣的。
判斷狀態(tài)可以用循環(huán),或者幀跳轉(zhuǎn)的循環(huán)檢測(cè)都可以。但是精靈的運(yùn)用原理也與flash不同,不方便寫那種幀里幀外的代碼。所以一般代碼都是寫在時(shí)間軸幀面板底下的代碼幀,那附近還有聲音幀的。
播放器沒寫過,以前都是用director開發(fā)多媒體教學(xué)軟件和教學(xué)游戲的。不過其實(shí)director功能特別強(qiáng)大值得好好研究,尤其后來3D的部分。不過director的·shockwave播放器普及度不高。
希望能幫到你 :)
局部變量
在函數(shù)體內(nèi)聲明的變量稱之為局部變量,它們的作用域只在函數(shù)體內(nèi),參數(shù)和返回值變量也是局部變量。
以下實(shí)例中 main() 函數(shù)使用了局部變量 a, b, c:
package main
import "fmt"
func main() {
/* 聲明局部變量 */
var a, b, c int
/* 初始化參數(shù) */
a = 10
b = 20
c = a + b
fmt.Printf ("結(jié)果: a = %d, b = %d and c = %d\n", a, b, c)
}
以上實(shí)例執(zhí)行輸出結(jié)果為:
結(jié)果: a = 10, b = 20 and c = 30
全局變量
在函數(shù)體外聲明的變量稱之為全局變量,全局變量可以在整個(gè)包甚至外部包(被導(dǎo)出后)使用。
全局變量可以在任何函數(shù)中使用,以下實(shí)例演示了如何使用全局變量:
package main
import "fmt"
/* 聲明全局變量 */
var g int
func main() {
/* 聲明局部變量 */
var a, b int
/* 初始化參數(shù) */
a = 10
b = 20
g = a + b
fmt.Printf("結(jié)果: a = %d, b = %d and g = %d\n", a, b, g)
}
以上實(shí)例執(zhí)行輸出結(jié)果為:
結(jié)果: a = 10, b = 20 and g = 30
Go 語言程序中全局變量與局部變量名稱可以相同,但是函數(shù)內(nèi)的局部變量會(huì)被優(yōu)先考慮。實(shí)例如下:
package main
import "fmt"
/* 聲明全局變量 */
var g int = 20
func main() {
/* 聲明局部變量 */
var g int = 10
fmt.Printf ("結(jié)果: g = %d\n", g)
}
以上實(shí)例執(zhí)行輸出結(jié)果為:
結(jié)果: g = 10
2021-10-22
每一個(gè)變量(常量、類型或函數(shù))在程序中都有一定的作用范圍。稱之為作用域。
Go語言在編譯時(shí)會(huì)檢查每一個(gè)變量是否使用過,未使用過的變量就會(huì)編譯錯(cuò)誤。
根據(jù)變量定義位置的不同,可以分為以下三個(gè)類型:
在函數(shù)體內(nèi)被聲明的變量稱之為局部變量,作用在函數(shù)體內(nèi),函數(shù)的參數(shù)和返回值變量都屬于局部變量。局部變量不會(huì)一直存在,在函數(shù)被調(diào)用時(shí)存在,函數(shù)調(diào)用結(jié)束后變量就會(huì)被銷毀,即生命周期。
例子:其中a、b均為局部變量,只會(huì)在main函數(shù)內(nèi)有效
在函數(shù)體外被聲明的變量稱之為全局變量,作用于所有源文件。不包含這個(gè)全局變量的源文件需要使用"import"關(guān)鍵字引入全局變量所在的源文件之后才能使用這個(gè)全局變量。
全局變量聲明必須以 var 關(guān)鍵字開頭,如果想要在外部包中使用全局變量的首字母必須大寫。
例如:global為全局在main2和main函數(shù)中都能使用
函數(shù)名后面的小括號(hào)里定義的變量, 用于接受來自調(diào)用函數(shù)的參數(shù)。用于接收調(diào)用該函數(shù)時(shí)傳入的參數(shù)。
例如:下面的例子中,第十七行a、b為sum函數(shù)定義的形參,用于傳入main函數(shù)中的AF、BF
Goroutine調(diào)度是一個(gè)很復(fù)雜的機(jī)制,下面嘗試用簡單的語言描述一下Goroutine調(diào)度機(jī)制,想要對(duì)其有更深入的了解可以去研讀一下源碼。
首先介紹一下GMP什么意思:
G ----------- goroutine: 即Go協(xié)程,每個(gè)go關(guān)鍵字都會(huì)創(chuàng)建一個(gè)協(xié)程。
M ---------- thread內(nèi)核級(jí)線程,所有的G都要放在M上才能運(yùn)行。
P ----------- processor處理器,調(diào)度G到M上,其維護(hù)了一個(gè)隊(duì)列,存儲(chǔ)了所有需要它來調(diào)度的G。
Goroutine 調(diào)度器P和 OS 調(diào)度器是通過 M 結(jié)合起來的,每個(gè) M 都代表了 1 個(gè)內(nèi)核線程,OS 調(diào)度器負(fù)責(zé)把內(nèi)核線程分配到 CPU 的核上執(zhí)行
模型圖:
避免頻繁的創(chuàng)建、銷毀線程,而是對(duì)線程的復(fù)用。
1)work stealing機(jī)制
當(dāng)本線程無可運(yùn)行的G時(shí),嘗試從其他線程綁定的P偷取G,而不是銷毀線程。
2)hand off機(jī)制
當(dāng)本線程M0因?yàn)镚0進(jìn)行系統(tǒng)調(diào)用阻塞時(shí),線程釋放綁定的P,把P轉(zhuǎn)移給其他空閑的線程執(zhí)行。進(jìn)而某個(gè)空閑的M1獲取P,繼續(xù)執(zhí)行P隊(duì)列中剩下的G。而M0由于陷入系統(tǒng)調(diào)用而進(jìn)被阻塞,M1接替M0的工作,只要P不空閑,就可以保證充分利用CPU。M1的來源有可能是M的緩存池,也可能是新建的。當(dāng)G0系統(tǒng)調(diào)用結(jié)束后,根據(jù)M0是否能獲取到P,將會(huì)將G0做不同的處理:
如果有空閑的P,則獲取一個(gè)P,繼續(xù)執(zhí)行G0。
如果沒有空閑的P,則將G0放入全局隊(duì)列,等待被其他的P調(diào)度。然后M0將進(jìn)入緩存池睡眠。
如下圖
GOMAXPROCS設(shè)置P的數(shù)量,最多有GOMAXPROCS個(gè)線程分布在多個(gè)CPU上同時(shí)運(yùn)行
在Go中一個(gè)goroutine最多占用CPU 10ms,防止其他goroutine被餓死。
具體可以去看另一篇文章
【Golang詳解】go語言調(diào)度機(jī)制 搶占式調(diào)度
當(dāng)創(chuàng)建一個(gè)新的G之后優(yōu)先加入本地隊(duì)列,如果本地隊(duì)列滿了,會(huì)將本地隊(duì)列的G移動(dòng)到全局隊(duì)列里面,當(dāng)M執(zhí)行work stealing從其他P偷不到G時(shí),它可以從全局G隊(duì)列獲取G。
協(xié)程經(jīng)歷過程
我們創(chuàng)建一個(gè)協(xié)程 go func()經(jīng)歷過程如下圖:
說明:
這里有兩個(gè)存儲(chǔ)G的隊(duì)列,一個(gè)是局部調(diào)度器P的本地隊(duì)列、一個(gè)是全局G隊(duì)列。新創(chuàng)建的G會(huì)先保存在P的本地隊(duì)列中,如果P的本地隊(duì)列已經(jīng)滿了就會(huì)保存在全局的隊(duì)列中;處理器本地隊(duì)列是一個(gè)使用數(shù)組構(gòu)成的環(huán)形鏈表,它最多可以存儲(chǔ) 256 個(gè)待執(zhí)行任務(wù)。
G只能運(yùn)行在M中,一個(gè)M必須持有一個(gè)P,M與P是1:1的關(guān)系。M會(huì)從P的本地隊(duì)列彈出一個(gè)可執(zhí)行狀態(tài)的G來執(zhí)行,如果P的本地隊(duì)列為空,就會(huì)想其他的MP組合偷取一個(gè)可執(zhí)行的G來執(zhí)行;
一個(gè)M調(diào)度G執(zhí)行的過程是一個(gè)循環(huán)機(jī)制;會(huì)一直從本地隊(duì)列或全局隊(duì)列中獲取G
上面說到P的個(gè)數(shù)默認(rèn)等于CPU核數(shù),每個(gè)M必須持有一個(gè)P才可以執(zhí)行G,一般情況下M的個(gè)數(shù)會(huì)略大于P的個(gè)數(shù),這多出來的M將會(huì)在G產(chǎn)生系統(tǒng)調(diào)用時(shí)發(fā)揮作用。類似線程池,Go也提供一個(gè)M的池子,需要時(shí)從池子中獲取,用完放回池子,不夠用時(shí)就再創(chuàng)建一個(gè)。
work-stealing調(diào)度算法:當(dāng)M執(zhí)行完了當(dāng)前P的本地隊(duì)列隊(duì)列里的所有G后,P也不會(huì)就這么在那躺尸啥都不干,它會(huì)先嘗試從全局隊(duì)列隊(duì)列尋找G來執(zhí)行,如果全局隊(duì)列為空,它會(huì)隨機(jī)挑選另外一個(gè)P,從它的隊(duì)列里中拿走一半的G到自己的隊(duì)列中執(zhí)行。
如果一切正常,調(diào)度器會(huì)以上述的那種方式順暢地運(yùn)行,但這個(gè)世界沒這么美好,總有意外發(fā)生,以下分析goroutine在兩種例外情況下的行為。
Go runtime會(huì)在下面的goroutine被阻塞的情況下運(yùn)行另外一個(gè)goroutine:
用戶態(tài)阻塞/喚醒
當(dāng)goroutine因?yàn)閏hannel操作或者network I/O而阻塞時(shí)(實(shí)際上golang已經(jīng)用netpoller實(shí)現(xiàn)了goroutine網(wǎng)絡(luò)I/O阻塞不會(huì)導(dǎo)致M被阻塞,僅阻塞G,這里僅僅是舉個(gè)栗子),對(duì)應(yīng)的G會(huì)被放置到某個(gè)wait隊(duì)列(如channel的waitq),該G的狀態(tài)由_Gruning變?yōu)開Gwaitting,而M會(huì)跳過該G嘗試獲取并執(zhí)行下一個(gè)G,如果此時(shí)沒有可運(yùn)行的G供M運(yùn)行,那么M將解綁P,并進(jìn)入sleep狀態(tài);當(dāng)阻塞的G被另一端的G2喚醒時(shí)(比如channel的可讀/寫通知),G被標(biāo)記為,嘗試加入G2所在P的runnext(runnext是線程下一個(gè)需要執(zhí)行的 Goroutine。), 然后再是P的本地隊(duì)列和全局隊(duì)列。
系統(tǒng)調(diào)用阻塞
當(dāng)M執(zhí)行某一個(gè)G時(shí)候如果發(fā)生了阻塞操作,M會(huì)阻塞,如果當(dāng)前有一些G在執(zhí)行,調(diào)度器會(huì)把這個(gè)線程M從P中摘除,然后再創(chuàng)建一個(gè)新的操作系統(tǒng)的線程(如果有空閑的線程可用就復(fù)用空閑線程)來服務(wù)于這個(gè)P。當(dāng)M系統(tǒng)調(diào)用結(jié)束時(shí)候,這個(gè)G會(huì)嘗試獲取一個(gè)空閑的P執(zhí)行,并放入到這個(gè)P的本地隊(duì)列。如果獲取不到P,那么這個(gè)線程M變成休眠狀態(tài), 加入到空閑線程中,然后這個(gè)G會(huì)被放入全局隊(duì)列中。
隊(duì)列輪轉(zhuǎn)
可見每個(gè)P維護(hù)著一個(gè)包含G的隊(duì)列,不考慮G進(jìn)入系統(tǒng)調(diào)用或IO操作的情況下,P周期性的將G調(diào)度到M中執(zhí)行,執(zhí)行一小段時(shí)間,將上下文保存下來,然后將G放到隊(duì)列尾部,然后從隊(duì)列中重新取出一個(gè)G進(jìn)行調(diào)度。
除了每個(gè)P維護(hù)的G隊(duì)列以外,還有一個(gè)全局的隊(duì)列,每個(gè)P會(huì)周期性地查看全局隊(duì)列中是否有G待運(yùn)行并將其調(diào)度到M中執(zhí)行,全局隊(duì)列中G的來源,主要有從系統(tǒng)調(diào)用中恢復(fù)的G。之所以P會(huì)周期性地查看全局隊(duì)列,也是為了防止全局隊(duì)列中的G被餓死。
除了每個(gè)P維護(hù)的G隊(duì)列以外,還有一個(gè)全局的隊(duì)列,每個(gè)P會(huì)周期性地查看全局隊(duì)列中是否有G待運(yùn)行并將其調(diào)度到M中執(zhí)行,全局隊(duì)列中G的來源,主要有從系統(tǒng)調(diào)用中恢復(fù)的G。之所以P會(huì)周期性地查看全局隊(duì)列,也是為了防止全局隊(duì)列中的G被餓死。
M0
M0是啟動(dòng)程序后的編號(hào)為0的主線程,這個(gè)M對(duì)應(yīng)的實(shí)例會(huì)在全局變量rutime.m0中,不需要在heap上分配,M0負(fù)責(zé)執(zhí)行初始化操作和啟動(dòng)第一個(gè)G,在之后M0就和其他的M一樣了
G0
G0是每次啟動(dòng)一個(gè)M都會(huì)第一個(gè)創(chuàng)建的goroutine,G0僅用于負(fù)責(zé)調(diào)度G,G0不指向任何可執(zhí)行的函數(shù),每個(gè)M都會(huì)有一個(gè)自己的G0,在調(diào)度或系統(tǒng)調(diào)用時(shí)會(huì)使用G0的??臻g,全局變量的G0是M0的G0
一個(gè)G由于調(diào)度被中斷,此后如何恢復(fù)?
中斷的時(shí)候?qū)⒓拇嫫骼锏臈P畔?,保存到自己的G對(duì)象里面。當(dāng)再次輪到自己執(zhí)行時(shí),將自己保存的棧信息復(fù)制到寄存器里面,這樣就接著上次之后運(yùn)行了。
我這里只是根據(jù)自己的理解進(jìn)行了簡單的介紹,想要詳細(xì)了解有關(guān)GMP的底層原理可以去看Go調(diào)度器 G-P-M 模型的設(shè)計(jì)者的文檔或直接看源碼
參考: ()
()
網(wǎng)站欄目:go語言全局變量命名規(guī)范,go語言定義變量
當(dāng)前URL:http://chinadenli.net/article38/hohpsp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供用戶體驗(yàn)、微信公眾號(hào)、網(wǎng)站維護(hù)、企業(yè)建站、品牌網(wǎng)站建設(shè)、外貿(mào)網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)