对于并发这个概念,我想大家都对它不会陌生,今天就从简单的火车站卖票问题出发,来谈谈并发。 首先声明本文的代码是golang(因为最近开始用的就是golang),对于其他的语言其实也是相通的,那么正式开始正题吧,首先我们来看看,卖一张票,总票数就减一,一般来说我们会这么写: package main import ( “fmt” “math/rand”
"time"
) var totle_tickets int = 1000 func sell_tickets() { for { if totle_tickets > 0 { time.Sleep(time.Duration(rand.Intn(5)) * time.Millisecond) totle_tickets– } else { break } } } func main() { t := time.Now() rand.Seed(time.Now().Unix()) sell_tickets() t2 := time.Since(t) fmt.Println(t2) } 执行耗时大概1-2秒,那么,我们来试试用协程加锁的方式: // bingfa_anquan project main.go package main import ( “fmt” “math/rand” “runtime” “sync” “time” ) var totle_tickets int = 1000 var ch chan bool var mutex sync.Mutex func sell_tickets(i int) { for { mutex.Lock() if totle_tickets > 0 { time.Sleep(time.Duration(5) * time.Millisecond) //rand.Intn(5)) totle_tickets– mutex.Unlock() } else { mutex.Unlock() ch <- true } } } func main() { t := time.Now() ch = make(chan bool) runtime.GOMAXPROCS(4) rand.Seed(time.Now().Unix()) for i := 0; i < 10; i++ { go sell_tickets(i) } for i := 0; i < 10; i++ { <-ch } t2 := time.Since(t) fmt.Println(t2) } 执行结果:耗时大概5秒多!很奇怪是不是,为什么协程执行却耗时更久了。下面我们来分析分析: 第一个程序: Main函数调用卖票函数,将1000张票卖完
第二个程序: Main函数中创建5个协程,将1000张票卖完 按照这个逻辑一个人卖票肯定是没有5个人卖票快的,但是这有个问题,那就是协程都是在操作tickets这个变量,而且该变量加锁了,那么这就意味着任意一个协程中,只要我在操作卖票这个操作,那么其他协程是不能去操作这个变量的,这样一来就变成哪个协程”运气好”轮到他,同时票已经没有锁定的时候,他才可以卖票,这个执行的时间=创建协程的时间+协程卖票的时间+通知主线程(票已经卖完了,可以结束了),所以时间反而变长了。 那么现在我们来思考下,如果开协程反而更慢了,那为什么还说多线程(协程)处理比单线程快呢?根源就在于这个例子有个不合理的地方,那就是事务并不复杂,同时协程(不管创建了多少个)等待条件不合理。一般来说,我们在使用协程处理服务器端的事务的时候,都会有很多操作,有些协程处理完自己的任务就把结果发走了,并不需要等待,有些协程等其他协程把结果给过来,一拿到就处理,处理完又把结果发走,也就是说大家(协程)做着自己的工作,并没有出现大家都要操作一个对象(变量),所有人都要等别人unlock的情况。 语言组织不是很好,但是感觉已经把想要表达的说清楚了,并不是所有的情况都适合并发,适合并发的情况可能是下面这样(举一个栗子帮助理解): 这里代码部分需要说明下,有些人可能会说,才1000张票就用并发,完全就是没意义,但是我这里只是举个例子,读者可以自己吧上面代码修改为一千万或是一个亿,但是,程序执行时间太长,没意义。这里读者可以把第二个程序加锁代码去掉,然后修改票数为一千万张你会发现,第二个程序执行的时间比第一个快大约10倍,协程开100个,执行速度就比第一个快大概100倍!但是结果会出现你所不希望看到的(自己运行看看)。 (编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|