线程在 Python 中是如何工作的,特定于 Python 线程的常见缺陷是什么?

我一直试图弄清楚线程在 Python 中是如何工作的,很难找到关于它们如何操作的好信息。我可能只是漏掉了一个链接什么的,但是看起来官方文件对这个问题并不是很全面,而且我也没能找到一篇好的文章。

据我所知,一次只能运行一个线程,并且活动线程每10条左右切换一次指令?

哪里有好的解释,或者你能提供一个吗?如果能够意识到在使用 Python 线程时遇到的常见问题,那将是非常好的。

44780 次浏览

Below is a basic threading sample. It will spawn 20 threads; each thread will output its thread number. Run it and observe the order in which they print.

import threading
class Foo (threading.Thread):
def __init__(self,x):
self.__x = x
threading.Thread.__init__(self)
def run (self):
print str(self.__x)


for x in xrange(20):
Foo(x).start()

As you have hinted at Python threads are implemented through time-slicing. This is how they get the "parallel" effect.

In my example my Foo class extends thread, I then implement the run method, which is where the code that you would like to run in a thread goes. To start the thread you call start() on the thread object, which will automatically invoke the run method...

Of course, this is just the very basics. You will eventually want to learn about semaphores, mutexes, and locks for thread synchronization and message passing.

Python's a fairly easy language to thread in, but there are caveats. The biggest thing you need to know about is the Global Interpreter Lock. This allows only one thread to access the interpreter. This means two things: 1) you rarely ever find yourself using a lock statement in python and 2) if you want to take advantage of multi-processor systems, you have to use separate processes. EDIT: I should also point out that you can put some of the code in C/C++ if you want to get around the GIL as well.

Thus, you need to re-consider why you want to use threads. If you want to parallelize your app to take advantage of dual-core architecture, you need to consider breaking your app up into multiple processes.

If you want to improve responsiveness, you should CONSIDER using threads. There are other alternatives though, namely microthreading. There are also some frameworks that you should look into:

Yes, because of the Global Interpreter Lock (GIL) there can only run one thread at a time. Here are some links with some insights about this:

From the last link an interesting quote:

Let me explain what all that means. Threads run inside the same virtual machine, and hence run on the same physical machine. Processes can run on the same physical machine or in another physical machine. If you architect your application around threads, you’ve done nothing to access multiple machines. So, you can scale to as many cores are on the single machine (which will be quite a few over time), but to really reach web scales, you’ll need to solve the multiple machine problem anyway.

If you want to use multi core, pyprocessing defines an process based API to do real parallelization. The PEP also includes some interesting benchmarks.

Use threads in python if the individual workers are doing I/O bound operations. If you are trying to scale across multiple cores on a machine either find a good IPC framework for python or pick a different language.

Try to remember that the GIL is set to poll around every so often in order to do show the appearance of multiple tasks. This setting can be fine tuned, but I offer the suggestion that there should be work that the threads are doing or lots of context switches are going to cause problems.

I would go so far as to suggest multiple parents on processors and try to keep like jobs on the same core(s).

One easy solution to the GIL is the multiprocessing module. It can be used as a drop in replacement to the threading module but uses multiple Interpreter processes instead of threads. Because of this there is a little more overhead than plain threading for simple things but it gives you the advantage of real parallelization if you need it. It also easily scales to multiple physical machines.

If you need truly large scale parallelization than I would look further but if you just want to scale to all the cores of one computer or a few different ones without all the work that would go into implementing a more comprehensive framework, than this is for you.

Note: wherever I mention thread i mean specifically threads in python until explicitly stated.

Threads work a little differently in python if you are coming from C/C++ background. In python, Only one thread can be in running state at a given time.This means Threads in python cannot truly leverage the power of multiple processing cores since by design it's not possible for threads to run parallelly on multiple cores.

As the memory management in python is not thread-safe each thread require an exclusive access to data structures in python interpreter.This exclusive access is acquired by a mechanism called GIL ( global interpretr lock ).

Why does python use GIL?

In order to prevent multiple threads from accessing interpreter state simultaneously and corrupting the interpreter state.

The idea is whenever a thread is being executed (even if it's the main thread), a GIL is acquired and after some predefined interval of time the GIL is released by the current thread and reacquired by some other thread( if any).

Why not simply remove GIL?

It is not that its impossible to remove GIL, its just that in prcoess of doing so we end up putting mutiple locks inside interpreter in order to serialize access, which makes even a single threaded application less performant.

so the cost of removing GIL is paid off by reduced performance of a single threaded application, which is never desired.

So when does thread switching occurs in python?

Thread switch occurs when GIL is released.So when is GIL Released? There are two scenarios to take into consideration.

If a Thread is doing CPU Bound operations(Ex image processing).

In Older versions of python , Thread switching used to occur after a fixed no of python instructions.It was by default set to 100.It turned out that its not a very good policy to decide when switching should occur since the time spent executing a single instruction can very wildly from millisecond to even a second.Therefore releasing GIL after every 100 instructions regardless of the time they take to execute is a poor policy.

In new versions instead of using instruction count as a metric to switch thread , a configurable time interval is used. The default switch interval is 5 milliseconds.you can get the current switch interval using sys.getswitchinterval(). This can be altered using sys.setswitchinterval()

If a Thread is doing some IO Bound Operations(Ex filesystem access or
network IO)

GIL is release whenever the thread is waiting for some for IO operation to get completed.

Which thread to switch to next?

The interpreter doesn’t have its own scheduler.which thread becomes scheduled at the end of the interval is the operating system’s decision. .