標籤:

不同的語言中多進程和多線程具體的原理是什麼?

python中由於全局鎖(GIL)的存在導致多線程並不能利用多核,看了一些資料說,java好像對多線程的處理是可以利用多核的硬體資源的(因為java直接調用的操作系統的多線程介面處理的)。不同的語言對多線程的處理是否能做到利用硬體,主要取決於編譯器或者解釋器對線程的包裝嗎?還有python中多進程是怎樣一個概念,不是說一個程序只有一個進程,進程裡面可以有多個線程,那麼python是怎麼在一個程序中用多個進程的?


Python中由於全局鎖(GIL)的存在導致多線程並不能利用多核,看了一些資料說,Java好像對多線程的處理是可以利用多核的硬體資源的(因為 Java直接調用的操作系統的多線程介面處理的)。不同的語言對多線程的處理是否能做到利用硬體,主要取決於編譯器或者解釋器對線程的包裝嗎?

我感覺你括弧里那句「因為 Java直接調用的操作系統的多線程介面處理的」好像並不是 Python 和 Java 在多線程上存在差異的原因,你去翻下 Python 源碼的 Thread.h 以及相關的一串頭文件就會發現,Python 中的線程在 Linux 上是調用了 pthread_create(),在 Windows上是調用了 CreateThread(),都是根正苗紅的原生線程

真正導致差異的,其實就是你一開始提到的 GIL,這個跟是不是「直接調用操作系統的多線程介面」沒啥關係。其實 Python 的 GIL 只在 CPython 這個實現中存在,Jython 是沒有的,Ruby 的 MRI 實現也有 GIL,而 JRuby 就沒有 GIL

Python 源碼中的 ceval.c 文件有 GIL 刷存在感的現場,比如第 256 行至第 290 行如下所示

void
PyEval_AcquireLock(void)
{
PyThread_acquire_lock(interpreter_lock, 1);
}

void
PyEval_ReleaseLock(void)
{
PyThread_release_lock(interpreter_lock);
}

void
PyEval_AcquireThread(PyThreadState *tstate)
{
if (tstate == NULL)
Py_FatalError("PyEval_AcquireThread: NULL new thread state");
/* Check someone has called PyEval_InitThreads() to create the lock */
assert(interpreter_lock);
PyThread_acquire_lock(interpreter_lock, 1);
if (PyThreadState_Swap(tstate) != NULL)
Py_FatalError(
"PyEval_AcquireThread: non-NULL old thread state");
}

void
PyEval_ReleaseThread(PyThreadState *tstate)
{
if (tstate == NULL)
Py_FatalError("PyEval_ReleaseThread: NULL thread state");
if (PyThreadState_Swap(NULL) != tstate)
Py_FatalError("PyEval_ReleaseThread: wrong thread state");
PyThread_release_lock(interpreter_lock);
}

其實如果嫌 Python 多線程有 GIL,也可以使用協程來搞嘛,比如 Stackless Python/greenlet/gevent 之類的

至於其他語言對多進程和多線程的實現,這個問題就太大了,不但包括各個語言的實現源碼,還涉及到各種線程/進程/並發模型,如果涉及到運行時,還有運行時那一套調度,等大牛補充好了

還有python中多進程是怎樣一個概念,不是說一個程序只有一個進程,進程裡面可以有多個線程,那麼python是怎麼在一個程序中用多個進程的?

結論:Python 通過封裝和調用操作系統提供的進程管理相關 API,實現多進程

下面皆為廢話,只是為了給題主展示一下以後遇到類似問題應該怎麼解決

Python 中的多進程通過 multiprocessing 模塊提供,於是先看 multiprocessing 的源碼

multiprocessing 中 fork.py 源碼的第 243 行至第 264 行如下所示

class Popen(object):
"""
Start a subprocess to run the code of a process object
"""
_tls = thread._local()

def __init__(self, process_obj):
# create pipe for communication with child
rfd, wfd = os.pipe()

# get handle for read end of the pipe and make it inheritable
rhandle = duplicate(msvcrt.get_osfhandle(rfd), inheritable=True)
os.close(rfd)

# start process
cmd = get_command_line() + [rhandle]
cmd = " ".join(""%s"" % x for x in cmd)
hp, ht, pid, tid = _subprocess.CreateProcess(
_python_exe, cmd, None, None, 1, 0, None, None, None
)
ht.Close()
close(rhandle)

可以看到,這裡是調用了_subprocess.CreateProcess()來產生一個新的進程

而關於 _subprocess 是什麼,StackOverflow 上有一個不錯的解釋(python - the _subprocess module)

根據上面這個解釋,下載 Python 源代碼,找到 _subprocess.c 文件,第 463 行至第 474 行如下所示

Py_BEGIN_ALLOW_THREADS
result = CreateProcess(application_name,
command_line,
NULL,
NULL,
inherit_handles,
creation_flags,
environment ? PyString_AS_STRING(environment) : NULL,
current_directory,
si,
pi);
Py_END_ALLOW_THREADS

於是可以看到,_subprocess.c 中實際是調用了 CreateProcess()函數,而這個函數是通過 _subprocess.c 開頭的 windows.h 頭文件間接包含進來的,是 Windows 操作系統提供的 API

關於 CreateProcess()的詳細說明,可以參考 MSDN(CreateProcess function (Windows))

於是,Windows 上的 Python 多進程,是通過直接調用操作系統的 API 實現的

除了 Windows,UNIX/Linux 上的 Python 多進程,其實也是通過直接調用操作系統的 API 實現的,直接看源碼

multiprocessing 中 fork.py 源碼的第 100 行至第 130 行如下所示

if sys.platform != "win32":
import time

exit = os._exit
duplicate = os.dup
close = os.close

#
# We define a Popen class similar to the one from subprocess, but
# whose constructor takes a process object as its argument.
#

class Popen(object):

def __init__(self, process_obj):
sys.stdout.flush()
sys.stderr.flush()
self.returncode = None

self.pid = os.fork()
if self.pid == 0:
if "random" in sys.modules:
import random
random.seed()
code = process_obj._bootstrap()
sys.stdout.flush()
sys.stderr.flush()
os._exit(code)

os.fork() 函數,很明顯了,雖然這個函數在 Windows 上的 Python 中似乎沒有?至少我沒有找到

如果說依然懷有「不是說一個程序只有一個進程,進程裡面可以有多個線程」這種疑問,建議活學活用操作系統相關課程

嗯,就這樣


GIL這個東西,跟實現有關。java的每一個對象都繼承了object類的六個方法,其中包括wait和notify.如果cpython也這麼干,就沒gil了。實際上,鳥叔曾搞出過這樣的python版本,但是由於單線程性能急劇下降,大家不樂意又換成Gil版本了。


有些語言可以調用操作系統API,最直接


第一個問題是的,python無法利用多核是因為其運行時,虛擬機解析器,是單線程的。(估計一開始就設計成這個樣子),於是任意個時刻虛擬機能運行上下文只有一份,也只在處理一份python虛擬機指令。多線程其實是假的,通過GIL鎖住了運行上下文狀態,切換上下文來模擬多線程

進程這個,和語言無關,和操作系統有關。多進程就是利用操作系統功能創建的進程而已,不是在同一個程序中。


多線程難道不是等io用么→_→

以及這玩意主要看實現是怎麼實現的

python也有多進程啊(*/ω\*)


推薦閱讀:

為什麼優礦的策略跑起來都很成功,是因為哪些因素沒有考慮到?
怎麼用 Python 編寫程序計算字元串中某個字元的個數?
使用anaconda以後再要使用不在conda環境中的包,要怎麼安裝?
Python 有哪些好的 Web 框架?
Python 2 和 Python 3 有哪些主要區別?

TAG:Python | 多線程 |