zoukankan      html  css  js  c++  java
  • Go语言之并发编程(一)

    轻量级线程(goroutine)

    在编写socket网络程序时,需要提前准备一个线程池为每一个socket的收发包分配一个线程。开发人员需要在线程数量和CPU数量间建立一个对应关系,以保证每个任务能及时地被分配到CPU上进行处理,同时避免多个任务频繁地在线程间切换执行而损失效率。

    虽然,线程池为逻辑编写者提供了线程分配的抽象机制。但是,如果面对随时随地可能发生的并发和线程处理需求,线程池就不是非常直观和方便了。能否有一种机制:使用者分配足够多的任务,系统能自动帮助使用者把任务分配到CPU上,让这些任务尽量并发运作。这种机制在Go语言中被称为goroutine。

    goroutine的概念类似于线程,但goroutine由Go程序运行时的调度和管理。Go程序会自动将goroutine中的任务分配给CPU。

    创建goroutine

    Go程序中使用go关键字为一个函数创建一个goroutine。一个函数可以被创建多个goroutine,一个goroutine必定对应一个函数。为一个普通函数创建goroutine的写法如下:go 函数名(参数列表)。

    函数名:要调用的函数名。

    参数列表:调用函数需要传入的参数。

    package main
    
    import (
    	"fmt"
    	"time"
    )
    
    func running(id, limit int) {
    
    	var times int
    	// 构建一个无限循环,当times与limit相等时才可以跳出循环
    	for {
    		times++
    		fmt.Printf("id: %d tick:%d
    ", id, times)
    		// 延时1秒
    		time.Sleep(time.Second)
    		if times == limit {
    			break
    		}
    	}
    
    }
    
    func main() {
    	// 并发执行程序
    	go running(1, 5)
    	go running(2, 6)
    	// 接受命令行输入, 不做任何事情,这里主要是等待两个协程执行完毕
    	var input string
    	fmt.Scanln(&input)
    }
    

      

    运行结果:  

    id: 2 tick:1
    id: 1 tick:1
    id: 1 tick:2
    id: 2 tick:2
    id: 2 tick:3
    id: 1 tick:3
    id: 2 tick:4
    id: 1 tick:4
    id: 2 tick:5
    id: 1 tick:5
    id: 2 tick:6
    

      

    代码执行后,命令行会不断输出tick直到times满足limit跳出循环,goroutine终止。同时可以使用fmt.Scanln接受用户输入,这两个环节可以同时进行。

    使用匿名函数创建goroutine

    go关键字后也可以为匿名函数或闭包启动goroutine。使用匿名函数或闭包创建goroutine时,除了将函数定义部分写在go的后面之外,还需要加上匿名函数的调用参数,格式如下::

    go func(参数列表){
    	函数体
    }(调用参数列表)
    

      

    参数列表:函数体内的参数变量列表。

    函数体:匿名函数的代码。

    调用参数列表:启动goroutine时,需要向匿名函数传递的调用参数。

    package main
    
    import (
    	"fmt"
    	"time"
    )
    
    func main() {
    
    	go func(id, limit int) {
    
    		var times int
    
    		for {
    			times++
    			fmt.Printf("id: %d tick:%d
    ", id, times)
    			time.Sleep(time.Second)
    			if times == limit {
    				break
    			}
    		}
    
    	}(1, 5)
    	var input string
    	fmt.Scanln(&input)
    }
    

      

    运行结果:

    id: 1 tick:1
    id: 1 tick:2
    id: 1 tick:3
    id: 1 tick:4
    id: 1 tick:5
    

      

    因为goroutine在main()函数结束时会一同结束,所以在main函数的末尾都加上fmt.Scanln函数,等待用户输入后才会结束main函数

    调整并发的运行性能(GOMAXPROCS)

    在Go程序运行时(runtime)实现了一个小型的任务调度器。这套调度器的工作原理类似于操作系统调度线程,Go程序调度器可以高效地将CPU资源分配给每一个任务。传统逻辑中,开发者需要维护线程池中线程与CPU核心数量的对应关系。同样的,Go中也可以通过runtime.GOMAXPROCS()函数做到,格式为:

    runtime.GOMAXPROCS(逻辑CPU数量)
    

      

    这里的逻辑CPU数量可以有如下几种数值:

    • <1:不修改任何数值。
    • =1:单核心执行。
    • >1:多核并行执行。

    一般情况下,可以使用runtime.NumCPU()查询CPU数量,并使用runtime.GOMAXPROCS()函数进行设置,例如:

    runtime.GOMAXPROCS(runtime.NumCPU())
    

      

    Go1.5版本之前,默认使用的是单核心执行。从Go1.5版本开始,默认执行上面语句以便让代码并发执行,最大效率地利用CPU。GOMAXPROCS同时也是一个环境变量,在应用程序启动前设置环境变量也可以起到相同的作用。

    并发和并行

    在讲解并发概念时,总会涉及另外一个概念并行。下面让我们来了解并发和并行之间的区别。

    • 并发(concurrency):把任务在不同的时间点交给处理器进行处理。在同一时间点,任务并不会同时运行。
    • 并行(parallelism):把每一个任务分配给每一个处理器独立完成。在同一时间点,任务一定是同时运行。

    两个概念的区别是:任务是否同时执行。举个栗子:

    • 并发:我们可以在播放音乐的同时浏览网站,如果我们的计算机是单核CPU,那么播放音乐和浏览网站是并发执行的,同一时刻CPU只能处理播放音乐或浏览网站。
    • 并行:如果我们的计算机不止一个CPU,那么播放音乐和浏览网站如果在不同的的CPU中执行,那么就是并行的。

    GO语言在GOMAXPROCS 数量与任务数量相等时,可以做到并行执行,但一般情况下都是并发执行。

    goroutine和coroutine的区别

    C#、Lua、Python语言都支持coroutine特性。coroutine与goroutine在名字上类似,都可以将函数或者语句在独立的环境中运行,但是它们之间有两点不同:

    • goroutine可能发生并行执行;
    • coroutine始终顺序执行。

    狭义地说,goroutine可能发生在多线程环境下,goroutine无法控制自己获取高优先度支持;coroutine始终发生在单线程,coroutine程序需要主动交出控制权,宿主才能获得控制权并将控制权交给其他coroutine。

    • goroutine间使用channel通信,coroutine使用yield和resume操作。
    • goroutine和coroutine的概念和运行机制都是脱胎于早期的操作系统。

    coroutine的运行机制属于协作式任务处理,早期的操作系统要求每一个应用必须遵守操作系统的任务处理规则,应用程序在不需要使用CPU时,会主动交出CPU使用权。如果开发者无意间或者故意让应用程序长时间占用CPU,操作系统也无能为力,表现出来的效果就是计算机很容易失去响应或者死机。

    goroutine属于抢占式任务处理,已经和现有的多线程和多进程任务处理非常类似。应用程序对CPU的控制最终还需要由操作系统来管理,操作系统如果发现一个应用程序长时间大量地占用CPU,那么用户有权终止这个任务。

  • 相关阅读:
    切割窗口url
    软键盘弹出底部被顶上去
    C语言字符串处理标准库函数的源码(转)
    slapd配置文件详述
    OPENLDAP安装配置方法
    const成员函数
    OPENLDAP概述
    当前比较有名的Xml数据库
    『转』使用 Scalable Vector Graphics 为 ASP.NET 构建基于 XML 的灵活、轻量的图像
    SqlCommand_ExecuteNonQuery 方法返回值为1的解释
  • 原文地址:https://www.cnblogs.com/beiluowuzheng/p/9901440.html
Copyright © 2011-2022 走看看