分类 编程语言 下的文章

# 未定义的参数标记
Hello
World


要注意的一些点是:

- 短标记和长标记后可以跟空格和等号再接参数,短标记也可以省略这两个符号直接加参数
- 不管长标记短标记后面都不能跟多个值

![](https://i.maqian.xin/2017/11/args.png)

一、channel

管道(channel)是golang中用于多协程通信的手段,也是go编程中常用到的数据类型。

虽然被称为管道,但是并非在《unix环境高级编程》中说的管道(fifo和pipe),go中的管道实际上是一种通过共享内存来实现的多线程通信方式,只是名字叫做管道而已。和fifo和pipe这两者没有任何关系。

创建一个管道的方法:

msg := make(chan string, 3)

其中的chan string表明管道是string类型,后面的3是管道容量。创建管道时如果指定了管道容量,管道就是一个有缓冲的管道。也可以省略3表示当前是一个无缓冲的管道,一般不建议使用无缓冲管道,可能导致程序阻塞。有缓冲管道和无缓冲管道的区别:

  1. 无缓冲管道:管道中没有元素的时候,读端会阻塞。管道有元素没有被读出的时候,写端阻塞。
  2. 有缓冲管道:管道中没有元素的嘶吼,读端会阻塞。管道中元素满了,写端阻塞。

管道的读写操作方法:

// 写管道
msg <- "HelloWorld"
// 读管道
val <- msg

示例

以下是一个无缓冲chan的执行示例,代码中分别创建两个goroutine,一个从管道读,一个往管道写,写端是在休眠一秒后才写。两个goroutine通过WaitGroup来同步结束状态:

func main() {
    var wg sync.WaitGroup
    msg := make(chan string)

    wg.Add(2)
    go func() {
        glog.Info("wait to msg...")
        glog.Info("receive msg: ", <-msg)
        wg.Done()
    }()

    go func() {
        time.Sleep(time.Second)
        msg <- "HelloWorld"
        wg.Done()
    }()

    wg.Wait()
}
代码中的输出使用glog来打印,方便输出更多信息。

输出结果:

管道接收方一共打印了两条日志,一条是协程刚启动准备接受消息时候的日志,一条是收到消息后的日志。对比两条日志的打印时间能看到中间间隔了1秒,这一秒刚好是管道发送方在睡眠,说明接收方在这一秒是在等待发送方发送数据,没有数据的时候被阻塞了。

使用无缓冲区管道千万要注意的就是阻塞,如果处理不当,读端没有及时读或者读端挂了,很可能就导致业务阻塞。

二、基于管道的异步调用

高并发场景中,一种经常用到的处理操作是异步调用,如何通过管道来实现异步调用呢?

以下是一段基于管道实现的异步调用示例,代码模拟了一次异步任务处理过程:主线程需要处理一个任务(耗时1秒),同时还要从数据库中读取一个字符串的数据。

func main() {
    var wg sync.WaitGroup
    msg := make(chan string)

    wg.Add(1)

    go func() { // 创建新的协程从数据库获取字符串
        glog.Info("service start!")
        msg <- "HelloWorld"
        glog.Info("service done!")
        wg.Done()
    }()

    glog.Info("doJob...")
    time.Sleep(time.Second) // 主线程执行任务

    glog.Info("service return: ", <-msg)
    wg.Wait()
}

程序运行结果:

从四条日志的打印时间来看,程序一共执行了大约1秒钟的时间,doJobservice start!几乎同时输出,两者在同时执行。过了一秒钟后,主函数执行完成,再从管道获取字符串数据,这样就省去了一次获取字符串数据的逻辑。假设获取字符串需要0.5秒,就节省了0.5秒的时间。

如果不使用这个异步的操作,那么整个函数的流程应该是:

  1. 主函数执行任务(1秒)
  2. 执行完成后再获取字符串数据(0.5秒)
  3. 函数结束(总耗时1.5秒)

使用异步之后,效率明显变高了。

三、有缓冲的管道

有缓冲的管道和无缓冲的管道最大的区别是往有缓冲管道内些数据时,如果管道还存在空间,写操作就不会阻塞,而无缓冲管道只要没有读端读出管道内数据,就会一直阻塞。

从上面的示例来看,msg是一个无缓冲区的管道,因此在创建的goroutine中写入数据后被阻塞,直到1秒后才打印出server done!。对这个协程而言,这1秒钟的过程无需阻塞,因为它的任务就是提取一个字符串消息然后放到管道,把数据放到管道后它的任务就完成了,可以直接结束了。没必要等到主函数读完才结束,否则很多协程都这样阻塞,纯粹浪费系统资源,降低性能。

那么这里就可以使用有缓冲区的管道来改进这一点,给管道设置一个缓冲区,新的协程往里面写数据就不会阻塞了,写完就能退出。代码中只需要修改msg创建时候的大小就可以了:

msg := make(chan string, 1)

执行结果:

可以看到,新协程启动和结束都是非常快速地,并没有等到一秒后才退出。同时主函数也在一秒后读到了字符串。

一、概述

python 提供了两个包来提供SMTP 邮件服务支持:smtplibemail ,前者系统内置,后者需要通过pip 或者其他方式手动安装。其中smtplib 提供邮件发送功能,email 负责邮件内容的构造。

二、smtplib库介绍

2.1 创建一个smtp连接对象

smtplib.SMTP(host, port)  # host是服务器地址,port是端口,连接成功将会返回一个stmp对象

或者

smtpObj = smtplib.SMTP()  # 创建一个空的smtp对象
smtpObj.connect(host, port)  # 连接服务

- 阅读剩余部分 -

一、概述

正则表达式,又称正规表示式、正规表示法、正规表达式、规则表达式、常规表示法(英语:Regular Expression,在代码中常简写为regex、regexp或RE),是计算机科学的一个概念。正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串。在很多文本编辑器里,正则表达式通常被用来检索、替换那些匹配某个模式的文本。

Regular Expression的“Regular”一般被译为“正则”、“正规”、“常规”。此处的“Regular”即是“规则”、“规律”的意思,Regular Expression即“描述某种规则的表达式”之意。

本篇将介绍正则表达式的基本语法,所有代码基于python 完成,环境:python2.7 + re 模块,python 操作正则的方法详见: python正则表达式的使用方法

- 阅读剩余部分 -

一、概述

re 模块是python官方提供的正则表达式模块,一些常用的方法如下:

re.match(pattern, string, pos, endpos)

string 中匹配pattern 规则,返回一个匹配对象。

re.search(pattern, string, pos, endpos)

string 中查找第一个满足规则pattern 的字符串,返回一个匹配对象。

re.findall(pattern, string, pos, endpos)

查找所有满足规则pattern 的字符串,结果将返回一个元组。

- 阅读剩余部分 -

一、概述

golang 生成随机数可以使用系统自带的math/rand  包。

二、使用方法

package main
import (
    "fmt"
    "math/rand"
)
func main() {
    for i:=0; i<10; i++ {
        fmt.Println(rand.Intn(100))
    }
}

这种情况下产生的随机数都是相同的,如果需要不同的随机数需要这样:

package main
import (
    "fmt"
    "time"
    "math/rand"
)
func main() {
    r := rand.New(rand.NewSource(time.Now().UnixNano()))
    for i:=0; i<10; i++ {
        fmt.Println(r.Intn(100))
    }
}

或者

package main
import (
    "fmt"
    "math/rand"
    "time"
)
func main(){
    rand.Seed(int64(time.Now().UnixNano()))
    for i := 0; i < 10; i++{
        fmt.Println(rand.Intn(1000))
    }
}

一、概述

上篇接介绍了文件的基本读写功能,这里将介绍文件指针及相关函数。

二、文件指针

文件指针是打开文件时就具有的属性,指向文件的位置,每次读文件或者写文件都会从指针位置开始,每次读写都会导致指针偏移相应的字节数。

默认情况下打开文件指针都是指向文件开头,只有当打开模式是追加方式(a a+ )时文件指针才指向文件结尾

- 阅读剩余部分 -

前言

记性越来越不好了,每次用python读写文件都会和golang搞混,今天好好抽个时间单独来复习一下,记录下来。

常用的文件函数:open() read() readline() readlines()`write() writelines()`

open()

open() 函数用来打开文件,定义为:def open(name, mode=None, buffering=None)

mode 是文件的打开属性:r r+ w w+ a a+ rb rb+ wb wb+ ab ab+

buffering 参数是缓冲,默认没有缓冲,设置1 表示有缓冲,一般不使用。

乱码问题的解决

UnicodeDecodeError: ''gbk'' codec can''t decode byte 0xac in position 372: illegal multibyte sequence

打开文件时加上编码标记即可:

open("data.txt", "w", encoding="utf-8")

- 阅读剩余部分 -