Python线程locking/class变量初始化混乱
Python thread locking/class variable initialisation confusion
我有一个 class 如果被多个线程访问,它会表现得很奇怪。这些线程是在 sklearn 的 GridSearch 训练期间启动的(jobs=3),所以我不知道它们是如何被调用的。
我的class本身大致是这样的:
from sklearn.base import BaseEstimator, TransformerMixin
import threading
class FeatureExtractorBase(BaseEstimator, TransformerMixin):
expensive_dependency = {}
lock = threading.lock()
is_loaded = False
@staticmethod
def load_dependencies():
FeatureExtractorBase.lock.acquire()
if not FeatureExtractorBase.is_loaded:
print('first request, start loading..')
# load dependencies, takes a while
FeatureExtractorBase.is_loaded = True
print('done')
else:
pass
FeatureExtractorBase.lock.release()
class ActualExtractor(FeatureExtractorBase):
def transform(self, data):
FeatureExtractorBase.load_dependencies()
# generate features from data using dependencies
return features
这个 class 使用了我希望的惰性初始化。立即初始化它会导致问题,我不能再这样做了。由于 class 在一个程序调用期间被重新初始化了几次,因此每次都在构造函数中初始化数据会浪费时间。现在,问题是
这不像我想要的那样工作,这是输出:
Starting training
Fitting 3 folds for each of 1 candidates, totalling 3 fits
first request, start loading..
first request, start loading..
first request, start loading..
done.
done.
done.
Done 1 jobs | elapsed: 56.2s
Done 3 out of 3 | elapsed: 1.0min finished
Starting evaluation
first request, start loading..
done.
不仅同时进入我认为会被锁定的区域的三个线程,一分钟后在测试期间,再次进入同一区域——尽管 is_loaded
应该设置为 True
在那个时候。
这是我第一次在 Python 中处理线程,而且我对 classes 仍然很笨拙,所以我确定我在这里做错了什么。但我看不到什么或在哪里。
由于全局解释器锁 (GIL),线程本身不会加速 python 进程,其瓶颈是 CPU 而不是 IO(read/write)。
为了真正获得加速,sklearn 使用多处理进行并行化。这与线程不同,因为对象被复制到一个单独的进程中,因此实际上有多个副本 class。
因此,每次启动新进程时,都会复制原始的、未初始化的 class,然后再次加载。
我认为这是 sklearn 用于网格搜索的主要并行化模块。
https://github.com/scikit-learn/scikit-learn/blob/master/sklearn/externals/joblib/parallel.py
我有一个 class 如果被多个线程访问,它会表现得很奇怪。这些线程是在 sklearn 的 GridSearch 训练期间启动的(jobs=3),所以我不知道它们是如何被调用的。
我的class本身大致是这样的:
from sklearn.base import BaseEstimator, TransformerMixin
import threading
class FeatureExtractorBase(BaseEstimator, TransformerMixin):
expensive_dependency = {}
lock = threading.lock()
is_loaded = False
@staticmethod
def load_dependencies():
FeatureExtractorBase.lock.acquire()
if not FeatureExtractorBase.is_loaded:
print('first request, start loading..')
# load dependencies, takes a while
FeatureExtractorBase.is_loaded = True
print('done')
else:
pass
FeatureExtractorBase.lock.release()
class ActualExtractor(FeatureExtractorBase):
def transform(self, data):
FeatureExtractorBase.load_dependencies()
# generate features from data using dependencies
return features
这个 class 使用了我希望的惰性初始化。立即初始化它会导致问题,我不能再这样做了。由于 class 在一个程序调用期间被重新初始化了几次,因此每次都在构造函数中初始化数据会浪费时间。现在,问题是 这不像我想要的那样工作,这是输出:
Starting training
Fitting 3 folds for each of 1 candidates, totalling 3 fits
first request, start loading..
first request, start loading..
first request, start loading..
done.
done.
done.
Done 1 jobs | elapsed: 56.2s
Done 3 out of 3 | elapsed: 1.0min finished
Starting evaluation
first request, start loading..
done.
不仅同时进入我认为会被锁定的区域的三个线程,一分钟后在测试期间,再次进入同一区域——尽管 is_loaded
应该设置为 True
在那个时候。
这是我第一次在 Python 中处理线程,而且我对 classes 仍然很笨拙,所以我确定我在这里做错了什么。但我看不到什么或在哪里。
由于全局解释器锁 (GIL),线程本身不会加速 python 进程,其瓶颈是 CPU 而不是 IO(read/write)。 为了真正获得加速,sklearn 使用多处理进行并行化。这与线程不同,因为对象被复制到一个单独的进程中,因此实际上有多个副本 class。 因此,每次启动新进程时,都会复制原始的、未初始化的 class,然后再次加载。
我认为这是 sklearn 用于网格搜索的主要并行化模块。 https://github.com/scikit-learn/scikit-learn/blob/master/sklearn/externals/joblib/parallel.py