Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading 使用多线程从URL下载文件_Multithreading_Curl_Coldfusion - Fatal编程技术网

Multithreading 使用多线程从URL下载文件

Multithreading 使用多线程从URL下载文件,multithreading,curl,coldfusion,Multithreading,Curl,Coldfusion,我需要从URL下载excel文件列表,并将其保存在文件夹中。(最多可以有200个左右的文件。)我从以下代码开始循环并下载每个文件: <cfloop query="idsToDownload"> <cfset fileURL = "https://myLink/#downloadID#" /><!--- link to an xlsx file ---> <cfexecute name="curl" arguments = "#fileUR

我需要从URL下载excel文件列表,并将其保存在文件夹中。(最多可以有200个左右的文件。)我从以下代码开始循环并下载每个文件:

<cfloop query="idsToDownload">
    <cfset fileURL = "https://myLink/#downloadID#" /><!--- link to an xlsx file --->
    <cfexecute name="curl" arguments = "#fileURL# -k" timeout="10" outputFile="#downloadID#.xlsx" />
</cfloop>
我假设这将与以前一样执行,只是每次下载都将在异步线程中运行。然而,当我运行这个程序时,什么都没有发生。我没有在页面上看到任何错误,但是ColdFusion临时文件中没有显示任何文件(就像简单的无线程cfloop一样)。这个代码有什么问题

编辑:

我还尝试了一个线程和一个下载,它工作正常:

<cfthread name="downloadFile" action="run">
    <cfset fileURL = "https://myLink/123" />
    <cfexecute name="curl" arguments = "#fileURL# -k" timeout="10" outputFile="123.xlsx" />
</cfthread>


因此,cfloop/cfthread组合似乎有问题…

我认为您需要让线程休眠一小段时间,以确保嵌套在cfthread标记中的代码在idsToDownload循环实际执行完之后才会执行

<cfloop query="idsToDownload">
<cfthread name="download_#downloadID#" action="run" downloadId="#idsToDownload.downloadId#" fileUrl="#idsToDownload.fileUrl#">
    <cfthread action="sleep" duration="500"/>
    <cfset fileURL = "https://myLink/#attributes.downloadID#" />
    <cfexecute name="curl" arguments = "#attributes.fileURL# -k" timeout="10" outputFile="#attributes.downloadID#.xlsx" />
</cfthread>


我不确定这是否有效,因为我从未使用过curl。但是让我知道会发生什么。

我认为您需要让线程休眠一小段时间,以确保嵌套在cfthread标记中的代码在idsToDownload循环实际完成执行之前不会执行

<cfloop query="idsToDownload">
<cfthread name="download_#downloadID#" action="run" downloadId="#idsToDownload.downloadId#" fileUrl="#idsToDownload.fileUrl#">
    <cfthread action="sleep" duration="500"/>
    <cfset fileURL = "https://myLink/#attributes.downloadID#" />
    <cfexecute name="curl" arguments = "#attributes.fileURL# -k" timeout="10" outputFile="#attributes.downloadID#.xlsx" />
</cfthread>


我不确定这是否有效,因为我从未使用过curl。但是请告诉我会发生什么。

我想您可能需要一种不同的方法来下载数百个文件。在目标服务器可能停止响应或阻止您(如果它不在您的控制之下)之前,每个文件的线程只会扩展到这么高。另外,如果您使用的是cURL,那么您将为每个线程生成一个子进程,因此它的资源相当多

相反,我会创建一个线程池并在它们之间分配工作。创建N个线程,并为每个线程提供一个要下载的文件列表。每个线程都将处理列表,您可以轻松地调优N,以实现最佳性能/资源使用权衡

上述方法的潜在缺点是,如果一个文件列表的下载速度比其他文件快得多,那么它将提前结束,剩下的工作将由更少的线程执行。您可以实现一个单一的工作跟踪器,每个线程调用它来获取下一个要下载的文件。只要它的getNextFile()方法被适当地同步,这将保持所有N个线程工作,直到有更多的工作要做

也可以考虑,如果你的下载和示例看起来一样简单,不要使用CURL。考虑CFHTTP或java HTTP客户端库之一,因为您不必每次下载生成一个进程。 编辑 关于运行现有代码,我能够构建一个相应的示例,该示例似乎执行正常(CF10/OSX):

线程测试…
开始#i#
完成。。。。
我能看到的唯一真正区别是,我显式地将参数传递给线程,并允许线程代码使用这些参数来组装URL(请参见urlNumber属性)。在我这么做之前,我看到了非常奇怪的结果:我得到了为值2-4而不是1-3编写的文件

我会确保线程需要的任何数据都是显式传递的。此外,cfexecute上的文档声明name属性需要是一个绝对路径,包括扩展名,但是没有扩展名,您的代码似乎工作正常

我已经包括了一个注释掉的例子,使用
实现与curl相同的功能。数百次启动任何外部流程几乎肯定无法实现规模化。调整上面的例子来划分一个列表,该列表上的每项工作都应该很简单

编辑2 下面的代码段实现了在可配置数量的线程之间分配工作负载:

Thread test...<br/>
<cfscript>
    urlCount=100;
    threads=5;
    urls=[];

    //utility function to split an array into a set of equal arrays
    function ArrayDivide(arr,divisor){
        divided=[];
        for(i=1;i<=divisor;i++){
            divided[i]=[];
        }
        for(i=1;i<=ArrayLen(arr);i++){
            ArrayAppend(divided[(i%divisor)+1],arr[i]);
            WriteOutput((i%divisor)+1 & "<br>");
        }
        return divided;
    }

    //Create a set of dummy URLs to test against
    //sleep.cfm waits for as long as it's asked to in order to simulate downloads taking a bit of time
    for(i=1;i<=urlCount;i++){
        urls[i]="http://localhost:8500/sleep.cfm?duration="&(i*50);
    }

    urlLists=ArrayDivide(urls,threads);

</cfscript>

<cfloop from="1" to="#threads#" index="i">  
    <cfoutput>Starting #i# <br/></cfoutput><cfflush>
    <cflog log="Application" text="#i# spawn">
    <cfthread action="run" name="dl-thread-#i#" urlList="#urlLists[i]#" threadNumber="#i#">
        <cflog log="Application" text="#threadNumber# start">
        <cfloop from="1" to="#ArrayLen(urlList)#" index="j">
            <cfhttp url="#urlList[j]#" file="#threadNumber#_#j#.html" path="#GetDirectoryFromPath(GetTemplatePath())#" method="get" />
        </cfloop>
        <cflog log="Application" text="#urlNumber# end">
</cfthread> 

</cfloop>
Done....
线程测试…
urlCount=100; 螺纹=5; URL=[]; //将数组拆分为一组相等数组的实用函数 函数ArrayDivide(arr,除数){ 除以=[];
对于(i=1;i我认为您可能需要一种不同的方法来下载数百个文件。在目标服务器可能停止响应或阻止您(如果它不在您的控制范围内)之前,每个文件的线程只会扩展到如此高的规模。此外,如果您使用cURL,那么您会为每个线程生成一个子进程,因此资源相当多

相反,我会创建一个线程池,并在它们之间分配工作。创建N个线程,并为每个线程提供一个要下载的文件列表。每个线程都将完成该列表,您可以轻松地调整N,以实现最佳性能/资源使用平衡

上述方法的潜在缺点是,如果一个文件列表的下载速度比其他文件快得多,那么它将提前结束,剩下的工作将由更少的线程执行。您可以实现一个工作跟踪器,每个线程调用该跟踪器来提取下一个要下载的文件。只要它的getNextFile()方法是适当同步的,这将使所有N个线程保持工作状态,直到有更多的工作要做

也可以考虑,如果下载的例子和看起来的一样简单,不要使用CURL。考虑CFHTTP或者java HTTP客户端库之一,因为你不必每次下载生成一个进程。 编辑 关于运行现有代码,我能够构建一个相应的示例,该示例似乎执行正常(CF10/OSX):

线程测试…
开始#i#
完成。。。。
我能看到的唯一真正的区别是,我显式地将参数传递给线程,并允许线程代码使用这些参数来组装URL(请参见urlNumber属性)。在此之前,我看到了非常奇怪的结果:我得到了为值2-4而不是1-3编写的文件

我会
Thread test...<br/>
<cfscript>
    urlCount=100;
    threads=5;
    urls=[];

    //utility function to split an array into a set of equal arrays
    function ArrayDivide(arr,divisor){
        divided=[];
        for(i=1;i<=divisor;i++){
            divided[i]=[];
        }
        for(i=1;i<=ArrayLen(arr);i++){
            ArrayAppend(divided[(i%divisor)+1],arr[i]);
            WriteOutput((i%divisor)+1 & "<br>");
        }
        return divided;
    }

    //Create a set of dummy URLs to test against
    //sleep.cfm waits for as long as it's asked to in order to simulate downloads taking a bit of time
    for(i=1;i<=urlCount;i++){
        urls[i]="http://localhost:8500/sleep.cfm?duration="&(i*50);
    }

    urlLists=ArrayDivide(urls,threads);

</cfscript>

<cfloop from="1" to="#threads#" index="i">  
    <cfoutput>Starting #i# <br/></cfoutput><cfflush>
    <cflog log="Application" text="#i# spawn">
    <cfthread action="run" name="dl-thread-#i#" urlList="#urlLists[i]#" threadNumber="#i#">
        <cflog log="Application" text="#threadNumber# start">
        <cfloop from="1" to="#ArrayLen(urlList)#" index="j">
            <cfhttp url="#urlList[j]#" file="#threadNumber#_#j#.html" path="#GetDirectoryFromPath(GetTemplatePath())#" method="get" />
        </cfloop>
        <cflog log="Application" text="#urlNumber# end">
</cfthread> 

</cfloop>
Done....