Python中的多线程文件传输?

Python中的多线程文件传输?,python,python-3.x,linux,multithreading,Python,Python 3.x,Linux,Multithreading,我手头有一个小小的特殊任务,我不知道如何最好地实现一个解决方案 我有三台工作站通过InfiniBand连接到运行Ubuntu20.04 LTS的NAS,带宽为40gbps。此NAS配备一个2TB NVMe SSD作为写缓存,7个RAID0单元作为主存储器 这些工作站将向该NAS输出原始数据供以后使用,每台计算机每天将输出大约6TB的数据文件,每个文件的大小从100-300 GB不等。为了防止网络变得过于拥挤,我让他们先将数据输出到NVMe缓存,然后我计划从那里分发数据文件—每个RAID0单元并发

我手头有一个小小的特殊任务,我不知道如何最好地实现一个解决方案

我有三台工作站通过InfiniBand连接到运行Ubuntu20.04 LTS的NAS,带宽为40gbps。此NAS配备一个2TB NVMe SSD作为写缓存,7个RAID0单元作为主存储器

这些工作站将向该NAS输出原始数据供以后使用,每台计算机每天将输出大约6TB的数据文件,每个文件的大小从100-300 GB不等。为了防止网络变得过于拥挤,我让他们先将数据输出到NVMe缓存,然后我计划从那里分发数据文件—每个RAID0单元并发分发一个文件,以最大化磁盘IO。例如,file1转到array0,file2转到array1,file3转到array2,依此类推

现在,我正在NAS端编写一个脚本(最好作为
systemd
服务,但我可以使用
nohup
)来监视缓存,并将文件发送到这些RAID阵列

这是我的想法,这非常接近我的目标,多亏了我的努力

现在,Python脚本可以成功地分发文件,但只能在范围(4)中i的
中的数字之后。例如,如果我将其设置为4,那么工人将对前4个文件使用相同的路径(array0),只有这样,他们才会开始在数组中迭代到1、2、3等等

有人能指出我如何分发这些文件吗?我认为我正朝着正确的方向前进,然而,我就是不明白为什么那些工人一开始就被困在同一个目录下


编辑:我有一个早期版本的代码,路径迭代正在生成过程中
threadWorkerCopy
。我现在让它移动到实际的worker函数,即
CopyWorker
。问题仍然存在。

问题在于,您不会在工作线程中生成
数组的新值,而只是在
线程工作副本中创建线程时才会生成

结果将取决于系统上的实际计时。每个工作线程在读取值时都将使用数组的值。这可能与
threadWorkerCopy
增加值或之后的操作同时进行,因此您可能会获得不同目录中的文件,或者所有文件都位于同一目录中

要为每个复制进程获取一个新的编号,必须在工作线程中增加
数组中的编号。在这种情况下,必须防止两个或多个线程同时访问
数组。您可以使用另一个锁来实现这一点

为了进行测试,我将目录列表替换为示例文件名的硬编码列表,并将复制替换为打印值

import queue, threading, os, time
import shutil

bfr_drive = '/home/test_folder' # cache
ext = ".dat" # data file extension
array = 0 # simluated array as t0-t6
fileList = [] # list of files to be moved from cache to storage
destPath = '/home/test_folder/t'
fileQueue = queue.Queue()


class ThreadedCopy:
    totalFiles = 0
    copyCount = 0
    array = 0
    lock = threading.Lock()
    lockArray = threading.Lock()

    def __init__(self):
        # directory listing replaced with hard-coded list for testing
        for file_name in [ 'foo.dat', 'bar.dat', 'baz.dat', 'a.dat', 'b.dat', 'c.dat', 'd.dat', 'e.dat', 'f.dat', 'g.dat' ] :
        #for file_name in os.listdir(bfr_drive):
            if file_name.endswith(ext):
                fileList.append(os.path.join(bfr_drive, file_name))
                fileList.sort()

        self.totalFiles = len(fileList)

        print (str(self.totalFiles) + " files to copy.")
        self.threadWorkerCopy(fileList)


    def CopyWorker(self):
        global array
        while True:
            fileName = fileQueue.get()

            with self.lockArray:
                myArray = array
                array += 1
                if array > 6:
                    array = 0

            # actual copying replaced with output for testing
            print('copying', fileName, destPath+str(myArray))
            #shutil.copy(fileName, destPath+str(myArray))

            with self.lock:
                self.copyCount += 1

                percent = (self.copyCount * 100) / self.totalFiles

                print (str(percent) + " percent copied.")

            # moved to end because otherwise main thread may terminate before the workers
            fileQueue.task_done()

    def threadWorkerCopy(self, fileNameList):
        for i in range(4):
            t = threading.Thread(target=self.CopyWorker)
            t.daemon = True
            t.start()

        for fileName in fileNameList:
            fileQueue.put(fileName)
        fileQueue.join()

ThreadedCopy()
这会打印如下内容(在不同运行之间可能会发生变化):

10个要复制的文件。
正在复制/home/test\u文件夹\a.dat/home/test\u文件夹/t0
10.0%被复制。
正在复制/home/test_文件夹\baz.dat/home/test_文件夹/t3
20.0%被复制。
正在复制/home/test\u文件夹\b.dat/home/test\u文件夹/t1
正在复制/home/test_文件夹\c.dat/home/test_文件夹/t4
正在复制/home/test_文件夹\bar.dat/home/test_文件夹/t2
复制/home/test_文件夹\d.dat/home/test_文件夹/t5
30.0%被复制。
正在复制/home/test_文件夹\e.dat/home/test_文件夹/t6
40.0%被复制。
正在复制/home/test_文件夹\f.dat/home/test_文件夹/t0
50.0%被复制。
正在复制/home/test_文件夹\foo.dat/home/test_文件夹/t1
60.0%被复制。
正在复制/home/test\u文件夹\g.dat/home/test\u文件夹/t2
70.0%被复制。
80.0%被复制。
90.0%被复制。
100.0%被复制。
注意事项:

我将行
fileQueue.task_done()
移动到
CopyWorker
的末尾。否则,我不会得到所有百分比输出行,有时还会收到错误消息

Fatal Python error: could not acquire lock for <_io.BufferedWriter name='<stdout>'> at interpreter shutdown, possibly due to daemon threads
致命的Python错误:在解释器关闭时无法获取的锁,可能是由于守护进程线程
也许您应该在主线程结束之前等待所有工作线程的结束

我没有检查代码中是否还有其他错误


更改问题中的代码后编辑

修改后的代码仍然存在以下问题:工作线程仍将在
fileQueue.task_done()
之后执行一些输出,以便主线程可以在工作线程之前结束


当工作线程访问
数组时,修改后的代码包含争用条件,因此该行为可能是意外的。

我是否正确理解您希望将第一个文件复制到
t0
,将第二个文件复制到
t1
,将第三个文件复制到
t2
等,第7个文件到
t6
8个文件到
t0
,依此类推?请澄清您的问题。当我测试您的代码时,
print(“当前数组是:“+str(数组))
的输出从1迭代到4,但是所有
CopyWorker
线程都将使用值4,以后不再开始迭代。@Bodo Yes。我想要文件1到阵列0、文件2到阵列1、文件3到阵列2等。请您的问题添加此澄清。关于你的问题的所有信息都应该在问题中,而不是在评论中。@Bodo这让问题变得更加奇怪。我认为发生的事情是迭代路径没有传递给
CopyWorker
。但是,即使是产生工作者本身的过程也是一个迭代循环。真的很奇怪,非常感谢!你的解释很透彻,很容易理解。我从未尝试过以多线程方式编写代码,这是我第一次尝试。没有你的帮助我做不到。
Fatal Python error: could not acquire lock for <_io.BufferedWriter name='<stdout>'> at interpreter shutdown, possibly due to daemon threads