欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Go語(yǔ)言開(kāi)發(fā)快速學(xué)習(xí)CGO編程

 更新時(shí)間:2023年04月06日 16:38:02   作者:路由器沒(méi)有路  
這篇文章主要為大家介紹了Go語(yǔ)言開(kāi)發(fā)之快速學(xué)習(xí)CGO編程,看了本文你就會(huì)發(fā)現(xiàn)CGO編程其實(shí)沒(méi)有想象的那么難,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

快速上手 CGO 程序

真實(shí)的 CGO 程序原理一般都比較復(fù)雜,但是在使用層面上來(lái)說(shuō),其實(shí)沒(méi)有想象的那么難。

今天我們可以由淺入深來(lái)看看一個(gè) CGO 程序該是怎么樣實(shí)現(xiàn)的?

如果要構(gòu)造一個(gè)簡(jiǎn)單的 CGO 程序,首先要忽視一些復(fù)雜的 CGO 特性,下面我們來(lái)快速上手一個(gè) CGO 程序。

基于 C 標(biāo)準(zhǔn)庫(kù)實(shí)現(xiàn)最簡(jiǎn)單的 CGO 程序

下面是我們構(gòu)建的最簡(jiǎn) CGO 程序:

// hello.go
package main
//#include <stdio.h>
import "C"
func main() {
    C.puts(C.CString("Hello, this is a CGO demo.\n"))
}

基于自己寫的 C 函數(shù)構(gòu)建 CGO 程序

上面就是使用了C標(biāo)準(zhǔn)庫(kù)中已有的函數(shù)來(lái)實(shí)現(xiàn)的一個(gè)簡(jiǎn)單的 CGO 程序。

下面我們?cè)賮?lái)看個(gè)例子。先自定義一個(gè)叫 SayHello 的 C 函數(shù)來(lái)實(shí)現(xiàn)打印,然后從 Go 語(yǔ)言環(huán)境中調(diào)用這個(gè) SayHello 函數(shù):

// hello.go
package main
/*
#include &lt;stdio.h&gt;
static void SayHello(const char* s) {
    puts(s);
}
*/
import "C"
func main() {
    C.SayHello(C.CString("Hello, World\n"))
}

除了 SayHello 函數(shù)是我們自己實(shí)現(xiàn)的之外,其它的部分和前面的例子基本相似。

我們也可以將 SayHello 函數(shù)放到當(dāng)前目錄下的一個(gè) C 語(yǔ)言源文件中(后綴名必須是.c)。因?yàn)槭蔷帉懺讵?dú)立的 C 文件中,為了允許外部引用,所以需要去掉函數(shù)的 static 修飾符。

// hello.c
#include <stdio.h>
void SayHello(const char* s) {
    puts(s);
}

然后在 CGO 部分先聲明 SayHello 函數(shù),其它部分不變:

// hello.go
package main
//void SayHello(const char* s);
import "C"
func main() {
    C.SayHello(C.CString("Hello, World\n"))
}

模塊化以上例子

在編程過(guò)程中,抽象和模塊化是將復(fù)雜問(wèn)題簡(jiǎn)化的通用手段。當(dāng)代碼語(yǔ)句變多時(shí),我們可以將相似的代碼封裝到一個(gè)個(gè)函數(shù)中;當(dāng)程序中的函數(shù)變多時(shí),我們將函數(shù)拆分到不同的文件或模塊中。

在前面的例子中,我們可以抽象一個(gè)名為 hello 的模塊,模塊的全部接口函數(shù)都聲明在 hello.h 頭文件中:

// hello.h
void SayHello(const char* s);

下面是 SayHello 函數(shù)的 C 語(yǔ)言實(shí)現(xiàn),對(duì)應(yīng) hello.c 文件:

// hello.c
#include "hello.h"
#include <stdio.h>
void SayHello(const char* s) {
    puts(s);
}

我們也可以用 C++語(yǔ)言來(lái)重新實(shí)現(xiàn)這個(gè) C 語(yǔ)言函數(shù):

// hello.cpp
#include <iostream>
extern "C" {
    #include "hello.h"
}
void SayHello(const char* s) {
    std::cout << s;
}

用 Go 實(shí)現(xiàn) C 函數(shù)并導(dǎo)出

其實(shí) CGO 不僅僅用于 Go 語(yǔ)言中調(diào)用 C 語(yǔ)言函數(shù),還可以用于導(dǎo)出 Go 語(yǔ)言函數(shù)給 C 語(yǔ)言函數(shù)調(diào)用。

在前面的例子中,我們已經(jīng)抽象一個(gè)名為 hello 的模塊,模塊的全部接口函數(shù)都在 hello.h 頭文件中定義:

// hello.h
void SayHello(char* s);

現(xiàn)在我們創(chuàng)建一個(gè) hello.go 文件,用 Go 語(yǔ)言重新實(shí)現(xiàn) C 語(yǔ)言接口的 SayHello 函數(shù):

// hello.go
package main
import "C"
import "fmt"
//export SayHello
func SayHello(s *C.char) {
    fmt.Print(C.GoString(s))
}

我們通過(guò) CGO 的 //export SayHello 指令將 Go 語(yǔ)言實(shí)現(xiàn)的函數(shù) SayHello 導(dǎo)出為 C 語(yǔ)言函數(shù)。為了適配 CGO 導(dǎo)出的 C 語(yǔ)言函數(shù),我們禁止了在函數(shù)的聲明語(yǔ)句中的 const 修飾符。

通過(guò)面向 C 語(yǔ)言接口的編程技術(shù),我們不僅僅解放了函數(shù)的實(shí)現(xiàn)者,同時(shí)也簡(jiǎn)化的函數(shù)的使用者。現(xiàn)在我們可以將 SayHello 當(dāng)作一個(gè)標(biāo)準(zhǔn)庫(kù)的函數(shù)使用,如下:

// main.go
package main
//#include <hello.h>
import "C"
func main() {
    C.SayHello(C.CString("Hello, World\n"))
}

用 C 接口的方式實(shí)現(xiàn) Go 編程

簡(jiǎn)單來(lái)說(shuō)就是將上面例子中的幾個(gè)文件重新合并到一個(gè) Go 文件實(shí)現(xiàn),如下:

// main.go
package main
//void SayHello(char* s);
import "C"
import (
    "fmt"
)
func main() {
    C.SayHello(C.CString("Hello, World\n"))
}
//export SayHello
func SayHello(s *C.char) {
    fmt.Print(C.GoString(s))
}

雖然看起來(lái)全部是 Go 語(yǔ)言代碼,但是執(zhí)行的時(shí)候是先從 Go 語(yǔ)言的 main 函數(shù),到 CGO 自動(dòng)生成的 C 語(yǔ)言版本 SayHello 橋接函數(shù),最后又回到了 Go 語(yǔ)言環(huán)境的 SayHello 函數(shù)。這個(gè)代碼包含了 CGO 編程的精華。

CGO 的主要基礎(chǔ)參數(shù)

import "C" 語(yǔ)句說(shuō)明

如果在 Go 代碼中出現(xiàn)了 import "C" 語(yǔ)句則表示使用了 CGO 特性,緊跟在這行語(yǔ)句前面的注釋是一種特殊語(yǔ)法,里面包含的是正常的 C 語(yǔ)言代碼。當(dāng)確保 CGO 啟用的情況下,還可以在當(dāng)前目錄中包含 C/C++對(duì)應(yīng)的源文件。比如上面的例子。

#cgo 語(yǔ)句說(shuō)明

import "C" 語(yǔ)句前的注釋中可以通過(guò) #cgo 語(yǔ)句設(shè)置編譯階段和鏈接階段的相關(guān)參數(shù)。編譯階段的參數(shù)主要用于定義相關(guān)宏和指定頭文件檢索路徑。鏈接階段的參數(shù)主要是指定庫(kù)文件檢索路徑和要鏈接的庫(kù)文件。

比如:

// #cgo CFLAGS: -DADDR_DEBUG=1 -I./include
// #cgo LDFLAGS: -L/usr/local/lib -linet_addr
// #include <inet_addr.h>
import "C"

上面的代碼中,CFLAGS 部分,-D 部分定義了宏 ADDR_DEBUG,值為 1;-I 定義了頭文件包含的檢索目錄。LDFLAGS 部分,-L 指定了鏈接時(shí)庫(kù)文件檢索目錄,-l 指定了鏈接時(shí)需要鏈接 inet_addr 庫(kù)。

因?yàn)?C/C++遺留的問(wèn)題,C 頭文件檢索目錄可以是相對(duì)目錄,但是庫(kù)文件檢索目錄則需要絕對(duì)路徑。

為什么要引入 CGO

突破 Go 創(chuàng)建切片的內(nèi)存限制

由于 Go 語(yǔ)言實(shí)現(xiàn)的限制,我們無(wú)法在 Go 語(yǔ)言中創(chuàng)建大于 2GB 內(nèi)存的切片(可參考 makeslice 實(shí)現(xiàn)源碼)。不過(guò)借助 cgo 技術(shù),我們可以在 C 語(yǔ)言環(huán)境創(chuàng)建大于 2GB 的內(nèi)存,然后轉(zhuǎn)為 Go 語(yǔ)言的切片使用:

package main
/*
#include <stdlib.h>
void* makeslice(size_t memsize) {
    return malloc(memsize);
}
*/
import "C"
import "unsafe"
func makeByteSlize(n int) []byte {
    p := C.makeslice(C.size_t(n))
    return ((*[1 << 31]byte)(p))[0:n:n]
}
func freeByteSlice(p []byte) {
    C.free(unsafe.Pointer(&p[0]))
}
func main() {
    s := makeByteSlize(1<<32+1)
    s[len(s)-1] = 255
    print(s[len(s)-1])
    freeByteSlice(s)
}

例子中我們通過(guò) makeByteSlize 來(lái)創(chuàng)建大于 4G 內(nèi)存大小的切片,從而繞過(guò)了 Go 語(yǔ)言實(shí)現(xiàn)的限制。而 freeByteSlice 輔助函數(shù)則用于釋放從 C 語(yǔ)言函數(shù)創(chuàng)建的切片。

因?yàn)?C 語(yǔ)言內(nèi)存空間是穩(wěn)定的,基于 C 語(yǔ)言內(nèi)存構(gòu)造的切片也是穩(wěn)定的,不會(huì)因?yàn)?Go 語(yǔ)言棧的變化而被移動(dòng)。

方便在 Go 語(yǔ)言中接入使用 C/C++的軟件資源

CGO 提供了 golang 和 C 語(yǔ)言相互調(diào)用的機(jī)制。而在某些第三方庫(kù)可能只有 C/C++ 的實(shí)現(xiàn),也沒(méi)有必要用純 golang 重新實(shí)現(xiàn),因?yàn)榭赡芄ぷ髁勘容^大,比較耗時(shí),這時(shí)候 CGO 就派上用場(chǎng)了。

被調(diào)用的 C 代碼可以直接以源代碼形式提供或者打包靜態(tài)庫(kù)或動(dòng)態(tài)庫(kù)在編譯時(shí)鏈接。

這里推薦使用靜態(tài)庫(kù)的方式,這樣方便代碼隔離,也符合 Go 的哲學(xué)。

CGO 帶來(lái)的問(wèn)題

構(gòu)建時(shí)間變長(zhǎng)

當(dāng)你在 Go 包中導(dǎo)入 "C" 時(shí),go build 需要做更多的工作來(lái)構(gòu)建你的代碼。

  • 需要調(diào)用 cgo 工具來(lái)生成 C 到 Go 和 Go 到 C 的相關(guān)代碼。
  • 系統(tǒng)中的 C 編譯器會(huì)為軟件包中的每個(gè) C 文件進(jìn)行調(diào)用處理。
  • 各個(gè)編譯單元被合并到一個(gè) .o 文件中。
  • 生成的 .o 文件會(huì)通過(guò)系統(tǒng)的鏈接器,對(duì)其引用的共享對(duì)象進(jìn)行修正。

構(gòu)建變得復(fù)雜

在引入了 cgo 之后,你需要設(shè)置所有的環(huán)境變量,跟蹤可能安裝在奇怪地方的共享對(duì)象和頭文件等。

另外需要注意,Go 支持許多的平臺(tái),而 cgo 并不是。需要安裝 C 編譯器,而不僅僅是 Go 編譯器。而且可能還需要安裝你的項(xiàng)目所依賴的 C 語(yǔ)言庫(kù),這也是需要技術(shù)成本的。

Go 和 C 內(nèi)存模型不同

內(nèi)存管理變得復(fù)雜,C 是沒(méi)有垃圾收集的,而 go 有,兩者的內(nèi)存管理機(jī)制不同,可能會(huì)帶來(lái)內(nèi)存泄漏。

CGO 是 Go 語(yǔ)言和 C 語(yǔ)言的橋梁,它使二者在二進(jìn)制接口層面實(shí)現(xiàn)了互通,但是我們要注意因兩種語(yǔ)言的內(nèi)存模型的差異而可能引起的問(wèn)題。

如果在 CGO 處理的跨語(yǔ)言函數(shù)調(diào)用時(shí)涉及到了指針的傳遞,則可能會(huì)出現(xiàn) Go 語(yǔ)言和 C 語(yǔ)言共享某一段內(nèi)存的場(chǎng)景。

我們知道 C 語(yǔ)言的內(nèi)存在分配之后就是穩(wěn)定的,但是 Go 語(yǔ)言因?yàn)楹瘮?shù)棧的動(dòng)態(tài)伸縮可能導(dǎo)致棧中內(nèi)存地址的移動(dòng)(這是 Go 和 C 內(nèi)存模型的最大差異)。如果 C 語(yǔ)言持有的是移動(dòng)之前的 Go 指針,那么以舊指針訪問(wèn) Go 對(duì)象時(shí)會(huì)導(dǎo)致程序崩潰。

使用 C 靜態(tài)庫(kù)實(shí)現(xiàn)

CGO 在使用 C/C++資源的時(shí)候一般有三種形式:

  • 直接使用源碼;
  • 鏈接靜態(tài)庫(kù);
  • 鏈接動(dòng)態(tài)庫(kù)。

直接使用源碼就是在 import "C" 之前的注釋部分包含 C 代碼,或者在當(dāng)前包中包含 C/C++源文件。

鏈接靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)的方式比較類似,都是通過(guò)在 LDFLAGS 選項(xiàng)指定要鏈接的庫(kù)方式鏈接。這里主要關(guān)注在 CGO 中如何使用靜態(tài)庫(kù)的問(wèn)題。

具體實(shí)現(xiàn)

如果 CGO 中引入的 C/C++資源有代碼而且代碼規(guī)模也比較小,直接使用源碼是最理想的方式,但很多時(shí)候我們并沒(méi)有源代碼,或者從 C/C++源代碼開(kāi)始構(gòu)建的過(guò)程異常復(fù)雜,這種時(shí)候使用 C 靜態(tài)庫(kù)也是一個(gè)不錯(cuò)的選擇。

靜態(tài)庫(kù)因?yàn)槭庆o態(tài)鏈接,最終的目標(biāo)程序并不會(huì)產(chǎn)生額外的運(yùn)行時(shí)依賴,也不會(huì)出現(xiàn)動(dòng)態(tài)庫(kù)特有的跨運(yùn)行時(shí)資源管理的錯(cuò)誤。

我們先用純 C 語(yǔ)言構(gòu)造一個(gè)簡(jiǎn)單的靜態(tài)庫(kù)。我們要構(gòu)造的靜態(tài)庫(kù)名叫 sum,庫(kù)中只有一個(gè) sum_add 函數(shù),用于表示數(shù)論中的模加法運(yùn)算。sum 庫(kù)的文件都在 sum 目錄下。

sum/sum.h 頭文件只有一個(gè)純 C 語(yǔ)言風(fēng)格的函數(shù)聲明:

int sum_add(int a, int b);

sum/sum.c 對(duì)應(yīng)函數(shù)的實(shí)現(xiàn):

#include "sum.h"
int sum_add(int a, int b) {
    return a+b;
}

通過(guò)以下命令可以生成一個(gè)叫 libsum.a 的靜態(tài)庫(kù):

$ cd ./sum
$ gcc -c -o sum.o sum.c
$ ar rcs libsum.a sum.o

生成 libsum.a 靜態(tài)庫(kù)之后,放到當(dāng)前的lib目錄下,我們就可以在 CGO 中使用該資源了。

創(chuàng)建 main.go 文件如下:

package main
/*
#cgo CFLAGS: -I./sum
#cgo LDFLAGS: -L./lib -lsum
#include "sum.h"
*/
import "C"
import "fmt"
func main() {
    fmt.Println(C.sum_add(10, 5))
}

其中有兩個(gè) #cgo 命令,分別是編譯和鏈接參數(shù)。

CFLAGS 通過(guò) -I./sum 將 sum 庫(kù)對(duì)應(yīng)頭文件所在的目錄加入頭文件檢索路徑。

LDFLAGS 通過(guò) -L./lib 將編譯后 sum 靜態(tài)庫(kù)所在目錄加為鏈接庫(kù)檢索路徑,-lsum 表示鏈接 libsum.a 靜態(tài)庫(kù)。

需要注意的是,在鏈接部分的檢索路徑不能使用相對(duì)路徑(C/C++代碼的鏈接程序所限制)

實(shí)戰(zhàn)應(yīng)用

這里以一個(gè)實(shí)際案例(分兩塊代碼)來(lái)說(shuō)明 CGO 如何使用靜態(tài)庫(kù)的。案例實(shí)現(xiàn)的功能說(shuō)明:

  • c++ 代碼實(shí)現(xiàn)初始化配置、解析傳入的 mq 消息,并處理具體的邏輯
  • go 代碼實(shí)現(xiàn)初始化相關(guān)配置(mq 等)、監(jiān)聽(tīng)訂單消息等工作

C++ 代碼主要實(shí)現(xiàn)

#include <iostream>
extern "C"{
    int init(int logLevel, int disId);
    void RecvAndDealMessage(char* sbuf, int len);
}
// 初始化
int init(int logLevel, int disId)
{
    g_xmfDisId = disId;
     // 服務(wù)初始化
    if(CCGI_STUB_CNTL->Initialize() != 0)
    {
        printf("CCGI_STUB_CNTL->Init failed\n");
        return -1;
    }
    CCGI_STUB_CNTL->setTimeout(5);
    // 日志初始化
    std::string strModuleName = "xxxxxx";
    int iRet = MD_LOG->QuickInitForAPP(strModuleName.c_str(), MD_LOG_FILE_PATH, logLevel);
    if (iRet != 0)
    {
        printf("log init failed. module:%s logswitch:%d ret:%d", strModuleName.c_str(), logLevel, iRet);
        return 1;
    }
    else
    {
        printf("Init log Ok\n");
    }
    MD_COMM_LOG_DEBUG("Log Init Finished. level:%d", logLevel);
    return iRet;
}
// 處理消息數(shù)據(jù)
void RecvAndDealMessage(char* sbuf, int len)
{
    MD_COMM_LOG_DEBUG("Begin receive message...");
    MessageContainer oMsgCon;
	  char strbuf[1024];
	  if(len > 1024)
	  {
		  MD_COMM_LOG_ERR(MESSAGE_TOO_LONG, "len = %d, message too long.", len);
		  return ;
	  }
    snprintf(strbuf, 1024, "%s", sbuf);
    MD_COMM_LOG_DEBUG("recvmessage:[%s] len:[%d]", strbuf, len);
    //解析并處理收到的消息
    DealMsg(strbuf, oMsgCon);
}

Go 代碼主要實(shí)現(xiàn)

main 函數(shù)實(shí)現(xiàn):

package main
/*
#cgo LDFLAGS:  -lstdc++
#cgo LDFLAGS: -L../lib  -ldaemon_qiyegou_finacial_deal_listen
#cgo LDFLAGS: -L../lib  -llibrary_util -lcgistub -linet_addr -ljsoncpp
int init(int logLevel, int disId);
void RecvAndDealMessage(char* sbuf, int len);
 */
import "C"
func main()  {
  //解析參數(shù)
		if Init() {
			defer func() {
				if err := recover(); err != nil {
					md_log.Errorf(-100, nil, "panic:%v, stack:%v", err, string(debug.Stack()))
				}
			}()
			for {
				//業(yè)務(wù)處理
				run()
			}
		}
}

init 函數(shù)實(shí)現(xiàn):

func Init() bool {
	iniFile, err := ini.LoadFile(os.Args[1])
	if err != nil {
		fmt.Println("load config faild, config:", os.Args[1])
		return false
	}
	logswitch := iniFile.GetInt("biz","logswitch",255)
	md_log.Init(DAEMON_NAME, iniFile.GetInt("biz","logswitch",255))
	md_log.Debugf("log init success!")
  // cgo 調(diào)用c++初始化函數(shù)
	ret := C.init(C.int(logswitch),C.int(xmf_dis_id))
	if  ret != 0 {
		fmt.Printf("init failed ret:%v \n", ret)
		return false
	}
	fmt.Println("initial success!")
	return true
}

run 函數(shù)代碼:

func run()  {
	var oConsumer rabbitmq.Consumer
	oConsumer.Init(Mqdns, MqexchangeName, Mqqueue, Mqroute)
	msgs, err := oConsumer.StartConsume(Mqqueue,false)
	if err != nil{
		fmt.Printf("oConsumer.StartConsume failed:%+v, arg:%+v \n",err, Mq
		return
	}
	for msg := range msgs{
		strMsg := string(msg.Body)
		msg.Ack(true)
    // 調(diào)用 C++ 處理消息的函數(shù)
		C.RecvAndDealMessage(C.CString(strMsg), C.int(len(strMsg))) //c++ 處理mq消息
	}
}

總結(jié)

通過(guò)以上實(shí)例說(shuō)明,可以知道CGO其實(shí)是C語(yǔ)言和Go語(yǔ)言混合編程的技術(shù),因此要想熟練地使用CGO是非常有必要要了解這兩門語(yǔ)言的。

任何技術(shù)和語(yǔ)言都有它自身的優(yōu)點(diǎn)和不足,Go語(yǔ)言不是銀彈,它無(wú)法解決全部問(wèn)題。而通過(guò)CGO可以做到以下幾點(diǎn):

  • 通過(guò)CGO可接入C/C++的世紀(jì)軟件遺產(chǎn)
  • 通過(guò)CGO可以用Go給其它系統(tǒng)寫C接口的共享庫(kù)
  • 通過(guò)CGO技術(shù)也可以讓Go語(yǔ)言編寫的代碼可以很好地融入現(xiàn)有的軟件生態(tài)

而現(xiàn)在的軟件確實(shí)大多數(shù)是建立在C/C++語(yǔ)言之上的。因此CGO可以說(shuō)是一個(gè)統(tǒng)籌兼?zhèn)涞募夹g(shù),是Go的一個(gè)重量級(jí)的技術(shù),也是值得任何一個(gè)Go語(yǔ)言開(kāi)發(fā)人員學(xué)習(xí)的。

以上就是Go語(yǔ)言開(kāi)發(fā)快速學(xué)習(xí)CGO編程的詳細(xì)內(nèi)容,更多關(guān)于Go語(yǔ)言開(kāi)發(fā)CGO編程的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • golang package time的用法具體詳解

    golang package time的用法具體詳解

    本篇文章主要介紹了golang package time的用法具體詳解,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-05-05
  • Golang的命名規(guī)范及最佳實(shí)踐(推薦!)

    Golang的命名規(guī)范及最佳實(shí)踐(推薦!)

    這篇文章主要給大家介紹了關(guān)于Golang的命名規(guī)范及最佳實(shí)踐的相關(guān)資料,命名規(guī)則涉及變量、常量、全局函數(shù)、結(jié)構(gòu)、接口、方法等的命名,文中介紹的非常詳細(xì),需要的朋友可以參考下
    2023-10-10
  • 詳解Go中defer與return的執(zhí)行順序

    詳解Go中defer與return的執(zhí)行順序

    Go?defer中改變r(jià)eturn的值會(huì)生效嗎,這就設(shè)計(jì)到了GO語(yǔ)言中defer與return哪個(gè)先執(zhí)行的問(wèn)題了,下面小編就通過(guò)簡(jiǎn)單的示例來(lái)和大家講講吧
    2023-07-07
  • 一文了解Go語(yǔ)言io.Copy函數(shù)

    一文了解Go語(yǔ)言io.Copy函數(shù)

    這篇文章主要為大家介紹了Go語(yǔ)言io.Copy函數(shù)使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-07-07
  • GoLang與Java各自生成grpc代碼流程介紹

    GoLang與Java各自生成grpc代碼流程介紹

    這篇文章主要介紹了GoLang與Java各自生成grpc代碼流程,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧
    2023-03-03
  • golang中值類型/指針類型的變量區(qū)別總結(jié)

    golang中值類型/指針類型的變量區(qū)別總結(jié)

    golang的值類型和指針類型receiver一直是大家比較混淆的地方,下面這篇文章主要給大家總結(jié)介紹了關(guān)于golang中值類型/指針類型的變量區(qū)別的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下。
    2017-12-12
  • Gin 框架快速創(chuàng)建靜態(tài)文件下載Web服務(wù)

    Gin 框架快速創(chuàng)建靜態(tài)文件下載Web服務(wù)

    本文主要介紹了Gin 框架快速創(chuàng)建靜態(tài)文件下載Web服務(wù),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • Go標(biāo)準(zhǔn)庫(kù)http與fasthttp服務(wù)端性能對(duì)比場(chǎng)景分析

    Go標(biāo)準(zhǔn)庫(kù)http與fasthttp服務(wù)端性能對(duì)比場(chǎng)景分析

    這篇文章主要介紹了Go標(biāo)準(zhǔn)庫(kù)http與fasthttp服務(wù)端性能比較,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-06-06
  • Golang極簡(jiǎn)入門教程(三):并發(fā)支持

    Golang極簡(jiǎn)入門教程(三):并發(fā)支持

    這篇文章主要介紹了Golang極簡(jiǎn)入門教程(三):并發(fā)支持,本文講解了goroutine線程、channel 操作符等內(nèi)容,需要的朋友可以參考下
    2014-10-10
  • Golang交叉編譯之跨平臺(tái)編譯使用詳解

    Golang交叉編譯之跨平臺(tái)編譯使用詳解

    這篇文章主要為大家介紹了Golang交叉編譯之跨平臺(tái)編譯使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10

最新評(píng)論