Python 访问同一对象时线程和全局解释器锁定

Python thread and global interpreter lock when access same object

我正在尝试了解 python 线程及其工作原理。

根据我的理解,我知道有GIL(Global Interpreter Lock)来防止两个线程同时访问内存。

这是非常合理的,尽管它会减慢程序速度。

但是下面的代码,显示出意想不到的结果。

import thread, time
mylist = [[0,1]]

def listTo300Elem(id):
    while len(mylist) < 300:
        mylist.append([id, mylist[-1][1]+1])

thread.start_new_thread(listTo300Elem, (1,))
thread.start_new_thread(listTo300Elem, (2,))
thread.start_new_thread(listTo300Elem, (3,))
thread.start_new_thread(listTo300Elem, (4,))
thread.start_new_thread(listTo300Elem, (5,))
thread.start_new_thread(listTo300Elem, (6,))
thread.start_new_thread(listTo300Elem, (7,))

time.sleep(5)

print mylist
print len(mylist)

结果是

[[0, 1], [1, 2], [1, 3], [1, 4], [1, 5], [1, 6], [1, 7], [1 , 8], [1, 9], [1, 10], [1, 11], [1, 12], [1, 13], [1, 14], [1, 15], [1, 16] ], [1, 17], [1, 18], [1, 19], [1, 20], [1, 21], [1, 22], [1, 23], [1, 24], [1, 25], [1, 26], [1, 27], [1, 28], [1, 29], [1, 30], [1, 31], [1, 32], [1 , 33], [1, 34], [1, 35], [1, 36], [1, 37], [1, 38], [1, 39], [1, 40], [1, 41] ], [1, 42], [1, 43], [1, 44], [1, 45], [1, 46], [1, 47], [1, 48], [1, 49], [1, 50], [1, 51], [1, 52], [1, 53], [1, 54], [1, 55], [1, 56], [1, 57], [1 , 58], [1, 59], [1, 60], [1, 61], [1, 62], [1, 63], [1, 64], [1, 65], [1, 66] ], [1, 67], [1, 68], [1, 69], [1, 70], [1, 71], [1, 72], [2, 73], [2, 74], [2, 75], [2, 76], [2, 77], [2, 78], [2, 79], [5, 80], [5, 81], [5, 82], [5 , 83], [5, 84], [5, 85], [5, 86], [5, 87], [5, 88], [5, 89], [5, 90], [5, 91 ], [5, 92], [5, 93], [5, 94], [3, 95], [3, 96], [3, 97], [3, 98], [3, 99], [3, 100], [3, 101], [3, 102], [3, 103], [3, 104], [3, 105], [3, 106], [3, 107], [3 , 108], [3, 109], [3, 110], [ 3, 111], [3, 112], [3, 113], [3, 114], [3, 115], [3, 116], [3, 117], [7, 118], [7, 119], [7, 120], [7, 121], [7, 122], [7, 123], [7, 124], [2, 80], [2, 81], [2, 82] , [2, 83], [2, 84], [2, 85], [2, 86], [2, 87], [2, 88], [2, 89], [2, 90], [ 2, 91], [2, 92], [2, 93], [2, 94], [2, 95], [2, 96], [2, 97], [2, 98], [2, 99], [2, 100], [2, 101], [2, 102], [2, 103], [2, 104], [2, 105], [2, 106], [2, 107] , [2, 108], [2, 109], [2, 110], [2, 111], [2, 112], [2, 113], [2, 114], [2, 115], [ 2, 116], [2, 117], [2, 118], [2, 119], [2, 120], [2, 121], [2, 122], [2, 123], [2, 124], [2, 125], [2, 126], [2, 127], [2, 128], [2, 129], [2, 130], [2, 131], [2, 132] , [2, 133], [2, 134], [2, 135], [2, 136], [2, 137], [2, 138], [2, 139], [2, 140], [ 2, 141], [7, 125], [7, 126], [7, 127], [7, 128], [7, 129], [7, 130], [7, 131], [7, 132], [7, 133], [7, 134], [7, 135], [7, 136], [7, 137], [7, 138], [7, 139], [7, 140] , [7, 141], [7, 142], [7, 143], [7, 144], [7, 145], [7, 146], [7, 147], [7, 148], [ 7, 149], [7, 150], [ 7, 151], [7, 152], [7, 153], [7, 154], [7, 155], [7, 156], [7, 157], [7, 158], [7, 159], [7, 160], [7, 161], [7, 162], [7, 163], [7, 164], [7, 165], [7, 166], [7, 167] , [7, 168], [7, 169], [7, 170], [7, 171], [6, 172], [6, 173], [6, 174], [6, 175], [ 6, 176], [6, 177], [6, 178], [3, 179], [3, 180], [3, 181], [3, 182], [3, 183], [3, 184], [3, 185], [3, 186], [3, 187], [3, 188], [3, 189], [3, 190], [3, 191], [3, 192] , [3, 193], [3, 194], [3, 195], [3, 196], [3, 197], [3, 198], [3, 199], [3, 200], [ 3, 201], [7, 202], [7, 203], [7, 204], [7, 205], [7, 206], [7, 207], [7, 208], [7, 209], [1, 210], [1, 211], [1, 212], [1, 213], [1, 214], [1, 215], [1, 216], [1, 217] , [1, 218], [1, 219], [1, 220], [1, 221], [1, 222], [1, 223], [1, 224], [1, 225], [ 1, 226], [1, 227], [1, 228], [1, 229], [1, 230], [1, 231], [1, 232], [1, 233], [1, 234], [1, 235], [1, 236], [1, 237], [1, 238], [3, 239], [5, 240], [2, 142], [6, 179] ] 304

根据我的理解,由于 GIL,结果必须是有序的,但它们不是。

我能得到这个例子的解释和进一步研究的项目吗?

你期待什么?那个线程添加一个项目,然后是另一个线程等等?为什么这么多线程,如果他们一次工作?线程试图同时处理一个对象。但是由于GIL不是做并行计算的好东西,所以他们做的很丑。

为了更好地理解 GIL 的工作原理,您可以添加日志记录。

logging.basicConfig(format="%(levelname)-8s [%(asctime)s] %(threadName)-12s %(message)s", level=logging.DEBUG, filename='log.log')

def listTo300Elem(id):
    list_len = len(mylist)
    while list_len < 300:
        item = mylist[-1][1]+1]
        mylist.append([id, item])
        logging.debug('Len = {}, item {} added'.format(list_len, item))
        list_len = len(mylist)
    logging.debug('Len = {}, exit'.format(list_len, item))

因此,python 中的线程并不适用于所有情况。