Python多线程使用每个线程不同的设置

时间:2015-06-25 12:15:05

标签: python multithreading python-2.7

我对Python相对较新,并且在大约7年前最后一次触及C中的线程和进程,所以请在回复中将我视为新手。

我在Linux上使用Python 2.7.6。

我正在尝试查询(以及稍后从中下载)在线存档,该存档仅允许每个注册用户一个连接并且非常慢。它有自己的查询API,所以我不会进入那个。我打算在并行线程中执行查询和稍后的下载,每个用户帐户一个。 (为了记录,我没有欺骗系统,所有帐户都是真正的用户!)

accounts = ['user1','pass1','user2','pass2'...]
queries = ['query1','query2','query3',..., 'queryN' ]

numQueries = len(queries) 
numAc = len(accounts)/2

if numQueries < numAc:
  nThreads = numQueries 
else
  nThreads = numAc # most likely situation

# example of function for the query 
def runQuery(user, passw, query):
  # here's the API bit

我见过的每个例子都在一个列表中运行。

所以,我不知所措。如果我们忘记所有关于帐户和约束并且只是运行不同的查询,我可以看到它将如何工作。

如何为每个帐户设置一个帖子并迭代查询/下载列表?记住我使用2.7。

我也被线程/流程问题所淹没,所以会非常感谢回复的清晰度。

---编辑 - 由于以下评论中的代码不可读,这就是我尝试过的:

ulock = thread.allocate_lock()

def runQuery(userQueue, ulock, queryQueue):
    query = queryQueue.get()
    with ulock:
        user = userQueue.popleft()
        userQueue.append(user)
        passw = userQueue.popleft()
        userQueue.append(passw)
    print 'The executed query will use: ' + user + ' ' + passw + ' ' + ' ' + query + '\n'

for t in nThreads:    
    thread.start_new_thread(runQuery, (userQueue, ulock, queryQueue,))

2 个答案:

答案 0 :(得分:2)

我认为如果您发现所有要执行的操作都是通过您拥有的用户帐户分发查询,那么您的问题会有一个更简单的答案,因此没有两个线程同时使用相同的凭据。

这意味着:将每个查询分配给用户帐户(因为您没有与查询一样多的帐户,在用户帐户上循环),然后按用户帐户分组,并让每个线程运行分配给单个用户帐户的所有查询。每个线程都会收到一组凭据,因此不存在并发问题。

"""
Distributes a number N of queries over a set of M user accounts
"""
from itertools import izip, cycle, groupby
from threading import Thread


def run_query(account, queries):
    """Run a number of queries under the same account"""
    user = account[0]
    passw = account[1]
    for query in queries:
        print 'The executed query will use: ' + user + ' ' + passw + ' ' + ' ' + query + '\n'


def main():
    """Distributes the queries and then runs them in threads"""
    accounts = [('user1', 'pass1'), ('user2', 'pass2'), ('userM', 'passM')]
    queries = ['query1', 'query2', 'query3', 'queryN']

    assignments = list(izip(cycle(accounts), queries))
    assignments = sorted(assignments, key=lambda (account, query): account)
    # [(('user1', 'pass1'), 'query1'), (('user1', 'pass1'), 'queryN'),
    #  (('user2', 'pass2'), 'query2'),
    #  (('userM', 'passM'), 'query3')]

    for account, assigned in groupby(assignments, lambda (account, query): account):
        queries = [item[1] for item in list(assigned)]
        Thread(target=run_query, args=(account, queries)).start()

if __name__ == '__main__':
    main()

一些注意事项:

  • 我将用户和密码分组为元组;它似乎有意义,即使它们在你的代码中被分开也不难做到。
  • 有N个查询和M个用户,其中N&gt; M.注意,在用户帐户上循环会导致查询N(最后一个)被分配给用户1。
  • 对分配进行排序是groupby函数的要求。
  • 使用itertools.groupby可能会非常棘手。请注意,结果的assigned部分是assigments元素的迭代器,因此每个元素都有一个account, query元组。重要的是,此迭代器仅返回单个帐户的元素;我们提取查询部分并在线程上运行它。
  • 顺便说一下,我发现threading.Thread要比threading.start_new_thread简单得多。无需join已启动的主题。
  • 没有Queues,没有互斥,没有任何东西。知道你的钥匙就是一切:)

答案 1 :(得分:1)

最简单的是(使用您当前的结构)使用旧的线程模块:

from thread import start_new_thread

start_new_thread(runQuery,(user1,pass1,query1,))
start_new_thread(runQuery,(user2,pass2,query2,))
start_new_thread(runQuery,(user3,pass3,query3,))

所有这些查询将并行运行,直到它们的函数runQuery返回。如果您不需要线程的反馈,则无需进行同步。

现在看来你确实需要同步,所以这样做:

定义将所有查询添加到的查询的队列:

from Queue import Queue

queryQueue = Queue()
queryQueue.put(query1)
queryQueue.put(query2)
queryQueue.put(query3)

现在通过对queryQueue的引用启动你的线程:

start_new_thread(runQuery,(user1,pass1,queryQueue,))
start_new_thread(runQuery,(user2,pass2,queryQueue,))
start_new_thread(runQuery,(user3,pass3,queryQueue,))

在你的run方法中,首先要做到这一点:

def runQuery(user, pass, queryQueue):
    query = queryQueue.get()

队列是任务安全的,这意味着它会为您完成所有必要的同步。