《Go语言从入门到精通》Go语言的并发处理_第1页
《Go语言从入门到精通》Go语言的并发处理_第2页
《Go语言从入门到精通》Go语言的并发处理_第3页
《Go语言从入门到精通》Go语言的并发处理_第4页
《Go语言从入门到精通》Go语言的并发处理_第5页
已阅读5页,还剩24页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Go语言的并发处理Go语言从入门到精通了解并发处理的基本概念与知识掌握Go语言中多任务的协同工作方式01CONCURENT02CONFLICT03TASKStarget目标掌握Go语言中避免并发共享冲突的主要方法目录导航11.1Go语言并发编程基础ContentsGo语言并发处理的优势

先天优势:是Go语言最重点解决的目标之一

极其简单便捷:一条语句启动线程

轻量级:轻松运行上千线程

语言级的并发支持:go关键字与通道(chan)数据类型硬件级支持:先天支持多核CPU上的调度简单的Goroutine启动方式func

function1()

{

a

:=

1

for

{

a++

fmt.Println(a)

}}

go

function1()

go关键字一条语句启动一个线程*

goroutine虽然与线程有所不同,但广义上我们还是可以称之为线程主线程func

main()

{

go

function1()

for

{ time.Sleep(100

*

time.Millisecond)

}

}main函数运行在主线程中主函数退出时,所有goroutine也将退出避免主线程占用过多CPU时间多核CPU并发分担cpuCores

:=

runtime.NumCPU()

//

获取实际CPU核数

runtime.GOMAXPROCS(2)

//

将可用CPU核数设为2目录导航11.2并发处理时的共享冲突Contents共享冲突var

valueG

int

=

0var

stop1

bool

=

falsevar

stop2

bool

=

false

func

routine1(countA

int,

stopA

*bool)

{

for

i

:=

0;

i

<

countA;

i++

{

valueG

=

valueG

+

2

}

*stopA

=

true}

func

main()

{

go

routine1(10000,

&stop1)

go

routine1(10000,

&stop2)

for

{

if

stop1

&&

stop2

{

break

}

}

fmt.Printf("valueG:

%v\n",

valueG)}多次运行结果不一致?并发安全共享安全就是保证在多任务并发处理时共享数据不会因共享冲突而导致错误需要在编程中加上控制机制来保证同一时刻只能由一个任务来访问该数据当变量或数据不会因多任务并发访问而出现异常时,我们就称之为是“共享安全”的目录导航11.3使用通道类型实现安全的数据共享Contents通道(chan)类型Go语言内置专门用于并发处理的数据类型:通道(chan)

chan类型可以理解成一个具备“共享安全”特性的“先入先出队列”*大多数其他语言是在标准库级别上支持线程的通道类型使用示例var

valueG

chan

intvar

stop1

bool

=

falsevar

stop2

bool

=

false

func

routine1(countA

int,

stopA

*bool)

{

for

i

:=

0;

i

<

countA;

i++

{

tmpC

:=

<-valueG

valueG

<-

(tmpC

+

2)

}

*stopA

=

true}

func

main()

{

valueG

=

make(chan

int,

1)

go

routine1(10000,

&stop1)

go

routine1(10000,

&stop2)

valueG

<-

0

for

{

if

stop1

&&

stop2

{

break

}

}

t.Printfln("valueG:

%v",

<-valueG)

close(valueG)注意->和<-操作符是“阻塞”的目录导航11.4使用通道类型实现安全的数据共享Contents通道类型使用示例var

tokenG

chan

boolvar

stop1

bool

=

falsevar

stop2

bool

=

false

func

routine1(countA

int,

stopA

*bool)

{

for

i

:=

0;

i

<

countA;

i++

{

<-tokenG

valueG

=

valueG

+

2

tokenG

<-

false

}

*stopA

=

true}

func

main()

{

tokenG

=

make(chan

bool,

1)

go

routine1(10000,

&stop1)

go

routine1(10000,

&stop2)

tokenG

<-

false

for

{

if

stop1

&&

stop2

{

break

}

}

t.Printfln("valueG:

%v",

valueG)

close(tokenG)获取到令牌的任务才能访问某些数据访问完毕之后交回令牌供其他任务使用目录导航11.5使用通道实现多任务的归并Contents多任务合并

并发编程中,主任务将任务拆分成子任务并等待所有子任务完成之后再进行下一步处理的过程称作“多任务的归并(或合并)”,英文中常用join来表示合并任务的主控线程//

调用多个addRoutine函数实现多goroutine计算累加和的函数//

将各个goroutine计算的结果之和加起来就是最终的总和func

addByGoroutine(countA

int)

float64

{

sumT

:=

0.0

//

lenT是每个goroutine需要计算的次数

lenT

:=

countA

/

goroutineCount

//

leftT是平均非给每个goroutine后还剩余需要计算的次数

leftT

:=

countA

-

(countA/goroutineCount)*goroutineCount

//

第一个goroutine将多计算leftT次,即lenT+leftT次

go

addRoutine(lenT

+

leftT)

//

其他goroutine将计算lenT次

for

i

:=

1;

i

<

goroutineCount;

i++

{

go

addRoutine(lenT)

}

//

从通道循环读取resultBuffer中的值,直到读满足够的个数

for

i

:=

0;

i

<

goroutineCount;

i++

{

sumT

+=

<-resultBuffer

}

//

返回最终的计算结果

return

sumT}此处将是阻塞地读取目录导航11.6用select实现多通道的归并Contents通过select监听多个通道//

调用多个addRoutine1函数和多个addRoutine2实现各自累加和并将两个累加和求相除结果的函数func

addByGoroutine(countA

int)

float64

{

sumT1

:=

0.0

sumT2

:=

0

lenT

:=

countA

/

goroutineCount//

lenT是每个goroutine需要计算的次数

leftT

:=

countA

-

(countA/goroutineCount)*goroutineCount

//

第一个goroutine将多计算leftT次,即lenT+leftT次

//

addRoutine1和addRoutine2都将被运行成同样个数的goroutine

go

addRoutine1(lenT

+

leftT)

go

addRoutine2(lenT

+

leftT)

for

i

:=

1;

i

<

goroutineCount;

i++

{//

其他goroutine将计算lenT次

go

addRoutine1(lenT)

go

addRoutine2(lenT)

}

//

从通道循环读取resultBuffer1或resultBuffer2中的值,直到读满足够的个数(应为2

*

goroutineCount个)

var

tmpF

float64

var

tmpC

int

for

i

:=

0;

i

<

goroutineCount*2;

i++

{

select

{

case

tmpF

=

<-resultBuffer1:

sumT1

+=

tmpF

case

tmpC

=

<-resultBuffer2:

sumT2

+=

tmpC

}

}

return

float64(sumT2)

/

sumT1//

返回最终的计算结果,为了计算类型一致,需要强制类型转换}尤其适合监听多个不同类型的通道时目录导航11.7select中实现超时中止Contents通过select监听多个通道

select

{

case

tmpF

=

<-resultBuffer1:

sumT1

+=

tmpF

case

tmpC

=

<-resultBuffer2:

sumT2

+=

tmpC

case

<-time.After(3

*

time.Second):

timeoutFlag

=

true

}time.After将返回一个定时触发的chan类型值目录导航11.8用单向通道自定义超时函数Contents单向通道var

c1

chan<-

int

//

定义一个只能写的通道

c1

=

make(chan<-

int,

10)

//

为只写通道设定容量//

直接简化式声明一个只读通道并设置容量

c2

:=

make(<-chan

float64,

10)

通过select监听多个通道//

休眠指定的秒数后,向通道中写入一个数值表示超时(数值本身不重要)//

chanA是只能写入的单向通道func

realTimeout1(secondsA

time.Duration,

chanA

chan<-

bool)

{

time.Sleep(secondsA

*

time.Second)

chanA

<-

true}

//

仅用于新建一个通道后启动真正的超时routine,并将该通道返回让select等待通道中有值func

timeout1(secondsA

time.Duration)

<-chan

bool

{

chan1

:=

make(chan

bool,

1)

//

传入realTimeout1的chan1被强制转换为只写通道类型

go

realTimeout1(secondsA,

(chan<-

bool)(chan1))

return

(<-chan

bool)(chan1)//

返回时将chan1强制转换为只读通道类型}…

select

{

case

tmpC

:=

<-resultBuffer1G:

totalSumT

+=

tmpC

case

tmpC

:=

<-resultBuffer2G:

totalSumT

+=

tmpC

case

<-timeout1(2):

//

超时时间是2秒

totalSumT

=

0

break

ForLabel

//

直接跳出循环而不是select结构

}是chan类型值就可以select目录导航11.9使用sync包进行传统的并发处理Contents用sync.WaitGroup实现goroutine归并var

groupG

sync.WaitGroup

func

addRoutine(countA

int)

{

defer

groupG.Done()

for

i

:=

0;

i

<

countA;

i++

{

valueG

=

valueG

+

2

}}

groupG.Add(2)

go

addRoutine(tim

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论