Il y a quelque temps, j'ai découvert un piège lors de l'utilisation de Python
pour mettre en œuvre des affaires. Pour être précis, c'est pour <.> profanes Un piège facile Python
class Mom(object): name = '' sons = []if __name__ == '__main__': m1 = Mom() m1.name = 'm1' m1.sons.append(['s1', 's2']) print '{} sons={}'.format(m1.name, m1.sons) m2 = Mom() m2.name = 'm2' m2.sons.append(['s3', 's4']) print '{} sons={}'.format(m2.name, m2.sons)复制代码
, qui contient un type chaîne Mom
et un type liste name
Attribut ; lors de l'utilisation de sons
et écrit une liste de données dans m1
, puis il crée une autre instance sons
et écrit également dans m2
Une autre liste de données ; est écrit. sons
, il est rare d'écrire Javaer
En voyant un tel code, la première sortie qui vient à l'esprit devrait être : Python
m1 sons=[['s1', 's2']] m2 sons=[['s3', 's4']]复制代码
m1 sons=[['s1', 's2']] m2 sons=[['s1', 's2'], ['s3', 's4']]复制代码
class Mom(object): name = '' def __init__(self): self.sons = []复制代码
. expérience pertinente, vous pouvez deviner la raison en comparant ces deux codes : Python
(c'est-à-dire chaque sortie que nous attendons), vous devez définir le variable dans le constructeur et accédez-y via Python
. Si self
dans Java
; ces données sont partagées par la classe, ce qui peut expliquer pourquoi la première situation se produit, car le static
est partagé par la classe sons
, il sera donc accumulé à chaque fois. Mom
peut obtenir l'effet de variables partagées dans la même classe via des variables de classe, le mode singleton peut-il être implémenté ? Python
de Python
pour contrôler dynamiquement la création de classes. metaclass
class Singleton(type): _instances = {} def __call__(cls, *args, **kwargs): if cls not in cls._instances: cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) return cls._instances[cls]复制代码
, puis nous l'utilisons comme Singleton
metaclass
class MySQLDriver: __metaclass__ = Singleton def __init__(self): print 'MySQLDriver init.....'复制代码
puisse contrôler Singleton
Cette classe est créée ; en fait, le MySQLDriver
dans Singleton
peut facilement comprendre le processus de création de ce singleton : __call__
_instances
dans Java
) peut être partagé dans toute la classe, quel que soit le nombre d'instances créées. map
__metaclass__ = Singleton
, sinon créez une instance Et réécrivez-le dans _instances
, qui ressemble à un conteneur _instances
. Spring
if __name__ == '__main__': m1 = MySQLDriver() m2 = MySQLDriver() m3 = MySQLDriver() m4 = MySQLDriver() print m1 print m2 print m3 print m4 MySQLDriver init..... <__main__.MySQLDriver object at 0x10d848790> <__main__.MySQLDriver object at 0x10d848790> <__main__.MySQLDriver object at 0x10d848790> <__main__.MySQLDriver object at 0x10d848790>复制代码
récemment, je souhaite également voir comment implémenter un singleton dans go
. go
type MySQLDriver struct { username string}复制代码
dans Java
), il est impossible de déclarer des variables partagées de classe comme class
et Python
;Java
Le le concept de go
n'existe pas dans la langue. static
import "fmt"type MySQLDriver struct { username string}var mySQLDriver *MySQLDriverfunc GetDriver() *MySQLDriver { if mySQLDriver == nil { mySQLDriver = &MySQLDriver{} } return mySQLDriver }复制代码
func main() { driver := GetDriver() driver.username = "cj" fmt.Println(driver.username) driver2 := GetDriver() fmt.Println(driver2.username) }复制代码
directement, Au lieu de cela, il est obtenu via la fonction MySQLDriver
Grâce à GetDriver()
, vous pouvez également voir que debug
et driver
font référence à la même adresse mémoire. driver1
, ce n'est pas si simple une fois qu'un accès simultané se produit. Java
, si plusieurs go
accèdent goroutine
en même temps, il y a une forte probabilité que plusieurs GetDriver()
instances soient créées. MySQLDriver
Le langage Java
fournit un go
simple pour accéder aux ressources critiques. api
var lock sync.Mutexfunc GetDriver() *MySQLDriver { lock.Lock() defer lock.Unlock() if mySQLDriver == nil { fmt.Println("create instance......") mySQLDriver = &MySQLDriver{} } return mySQLDriver }func main() { for i := 0; i < 100; i++ { go GetDriver() } time.Sleep(2000 * time.Millisecond) }复制代码
lock.Lock()defer lock.Unlock()复制代码
Exemple Ce ne sera que le cas. initialisé une fois. mySQLDriver
defer
类似于 Java
中的 finally
,在方法调用前加上 go
关键字即可开启一个协程。虽说能满足并发要求了,但其实这样的实现也不够优雅;仔细想想这里
mySQLDriver = &MySQLDriver{}复制代码
创建实例只会调用一次,但后续的每次调用都需要加锁从而带来了不必要的开销。
这样的场景每个语言都是相同的,拿 Java
来说是不是经常看到这样的单例实现:
public class Singleton { private Singleton() {} private volatile static Singleton instance = null; public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class){ if (instance == null) { instance = new Singleton(); } } } return instance; } }复制代码
这是一个典型的双重检查的单例,这里做了两次检查便可以避免后续其他线程再次访问锁。
同样的对于 go
来说也类似:
func GetDriver() *MySQLDriver { if mySQLDriver == nil { lock.Lock() defer lock.Unlock() if mySQLDriver == nil { fmt.Println("create instance......") mySQLDriver = &MySQLDriver{} } } return mySQLDriver }复制代码
和 Java
一样,在原有基础上额外做一次判断也能达到同样的效果。
但有没有觉得这样的代码非常繁琐,这一点 go
提供的 api
就非常省事了:
var once sync.Oncefunc GetDriver() *MySQLDriver { once.Do(func() { if mySQLDriver == nil { fmt.Println("create instance......") mySQLDriver = &MySQLDriver{} } }) return mySQLDriver }复制代码
本质上我们只需要不管在什么情况下 MySQLDriver
实例只初始化一次就能达到单例的目的,所以利用 once.Do()
就能让代码只执行一次。
查看源码会发现 once.Do()
也是通过锁来实现,只是在加锁之前利用底层的原子操作做了一次校验,从而避免每次都要加锁,性能会更好。
相信大家日常开发中很少会碰到需要自己实现一个单例;首先大部分情况下我们都不需要单例,即使是需要,框架通常也都有集成。
类似于 go
这样框架较少,需要我们自己实现时其实也不需要过多考虑并发的问题;摸摸自己肚子左上方的位置想想,自己写的这个对象真的同时有几百上千的并发来创建嘛?
不过通过这个对比会发现 go
的语法确实要比 Java
简洁太多,同时轻量级的协程以及简单易用的并发工具支持看起来都要比 Java
优雅许多;后续有机会再接着深入。
相关免费学习推荐:python视频教程
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!