Ale*_*xey 4 python multithreading
我想测试是否可以从两个线程追加到列表,但是输出混乱:
import threading
class myThread(threading.Thread):
def __init__(self, name, alist):
threading.Thread.__init__(self)
self.alist = alist
def run(self):
print "Starting " + self.name
append_to_list(self.alist, 2)
print "Exiting " + self.name
print self.alist
def append_to_list(alist, counter):
while counter:
alist.append(alist[-1]+1)
counter -= 1
alist = [1, 2]
# Create new threads
thread1 = myThread("Thread-1", alist)
thread2 = myThread("Thread-2", alist)
# Start new Threads
thread1.start()
thread2.start()
print "Exiting Main Thread"
print alist
Run Code Online (Sandbox Code Playgroud)
所以输出是:
Starting Thread-1
Exiting Thread-1
Starting Thread-2
Exiting Main Thread
Exiting Thread-2
[1[1, 2[, 1, 2, 23, , 34, 5, 6, ]4
, 5, , 3, 64, 5, ]6]
Run Code Online (Sandbox Code Playgroud)
为什么它如此凌乱并且列表不等于[1,2,3,4,5,6]?
摘要
为什么输出混乱?
==>因为线程可能通过执行一条print语句而产生部分结果
为什么aList不等于[1,2,3,4,5,6]?
==>因为的内容aList可能在读取和附加之间发生变化。
输出量
输出是混乱的,因为它是由python2的print语句从线程内部产生的,并且该print语句不是线程安全的。这意味着线程可以在print执行时屈服。在问题代码中,有多个线程正在打印,因此一个线程可以在打印时屈服,另一个线程可以开始打印,然后屈服,从而产生OP所看到的交错输出。IO操作(例如写入)stdout
在CPU方面非常慢,因此操作系统很可能会暂停执行IO的线程,因为线程正在等待硬件做某事。
例如,此代码:
import threading
def printer():
for i in range(2):
print ['foo', 'bar', 'baz']
def main():
threads = [threading.Thread(target=printer) for x in xrange(2)]
for t in threads:
t.start()
for t in threads:
t.join()
Run Code Online (Sandbox Code Playgroud)
产生以下交错输出:
>>> main()
['foo', 'bar'['foo', , 'bar', 'baz']
'baz']
['foo', ['foo', 'bar''bar', 'baz']
, 'baz']
Run Code Online (Sandbox Code Playgroud)
可以通过使用来防止交错行为lock:
def printer():
for i in range(2):
with lock:
print ['foo', 'bar', 'baz']
def main():
global lock
lock = threading.Lock()
threads = [threading.Thread(target=printer) for x in xrange(2)]
for t in threads:
t.start()
for t in threads:
t.join()
>>> main()
['foo', 'bar', 'baz']
['foo', 'bar', 'baz']
['foo', 'bar', 'baz']
['foo', 'bar', 'baz']
Run Code Online (Sandbox Code Playgroud)
清单内容
的最终内容aList将是[1, 2, 3, 4, 5, 6]如果语句
aList.append(aList[-1] + 1)
是原子执行的,也就是说,当前线程不会屈服于另一个线程,该线程也在读取并附加到aList。
但是,这不是线程的工作方式。在从aList值中读取最后一个元素或增加值后,线程可能会屈服,因此很有可能发生如下事件序列:
2从aList2从aList,则追加33从aList,则追加433从aList,则追加4这留aList为[1, 2, 3, 4, 3, 4]
与print语句一样,可以通过使线程lock在执行之前获取a来防止这种情况aList.append(aList[-1] + 1)
(请注意,该list.append方法在纯python代码中是 线程安全的,因此不存在附加值可能被损坏的风险。)
编辑:@kroltan 让我思考更多,我认为你的例子实际上比我最初想象的更线程安全。问题不在于总共的多个编写器线程,具体在于这一行:
\n\n\n\n\nalist.append(alist[-1]+1)
\n
不能保证该操作append会在完成后立即发生alist[-1],其他操作可能会交错进行。
这里有详细的解释:\n http://effbot.org/pyfaq/what-kinds-of-global-value-mutation-are-thread-safe.htm
\n\n\n\n\n当其他对象的引用计数达到零时,替换其他对象的操作可能会调用这些其他对象\xe2\x80\x99 del方法,这可能会影响事物。对于字典和列表的大规模更新尤其如此。如有疑问,请使用互斥体!
\n
原答案:
\n\n\n\n这是未定义的行为,因为您有多个线程写入同一位内存 - 因此您观察到的“混乱”输出。
\n\n\n\n我想测试是否可以从两个线程附加到列表,但我得到的输出很混乱
\n\n我想你已经成功地测试了这个,答案是否定的。\n 关于 SO 有很多更详细的解释:\n /sf/answers/416011921/
\n