我有一个urls表,我需要循环,下载每个文件,更新表并返回结果。我想一次最多运行10次下载,所以我想考虑如下使用代理:
DataTable photos;
bool scanning = false,
complete = false;
int rowCount = 0;
public delegate int downloadFileDelegate();
public void page_load(){
photos = Database.getData...
downloadFileDelegate d = downloadFile;
d.BeginInvoke(downloadFileComplete, d);
d.BeginInvoke(downloadFileComplete, d);
d.BeginInvoke(downloadFileComplete, d);
d.BeginInvoke(downloadFileComplete, d);
d.BeginInvoke(downloadFileComplete, d);
while(!complete){}
//handle results...
}
int downloadFile(){
while(scanning){} scanning = true;
DataRow r;
for (int ii = 0; ii < rowCount; ii++) {
r = photos.Rows[ii];
if ((string)r["status"] == "ready"){
r["status"] = "running";
scanning = false; return ii;
}
if ((string)r["status"] == "running"){
scanning = false; return -2;
}
}
scanning = false; return -1;
}
void downloadFileComplete(IAsyncResult ar){
if (ar == null){ return; }
downloadFileDelegate d = (downloadFileDelegate)ar.AsyncState;
int i = d.EndInvoke(ar);
if (i == -1){ complete = true; return; }
//download file...
//update row
DataRow r = photos.Rows[i];
r["status"] = "complete";
//invoke delegate again
d.BeginInvoke(downloadFileComplete, d);
}
然而,当我运行它时,它需要花费相同的时间来运行5,而我预计它需要花费5倍的速度。
有什么想法吗?
答案 0 :(得分:4)
你看起来好像在尝试使用无锁同步(使用while(scanning)
来检查在函数开始时设置的布尔值,并在结束时重置),但所有这些成功的做法只是运行一个一次检索。
scanning
标志(及相关逻辑)。volatile
(否则它们的读取可以被缓存,你可以无休止地等待)DataRow
)中的值必须在UI线程上进行。您必须在Control.Invoke
或Control.BeginInvoke
调用中包装这些操作,否则您将跨越线程边界与控件进行交互。BeginInvoke
会返回AsyncWaitHandle
。将此用于您的操作全部完成时将要发生的逻辑。像这样的东西-
WaitHandle[] handles = new WaitHandle[]
{
d.BeginInvoke(...),
d.BeginInvoke(...),
d.BeginInvoke(...),
d.BeginInvoke(...),
d.BeginInvoke(...)
}
WaitHandle.WaitAll(handles);
这将导致调用线程阻塞,直到所有操作完成。
答案 1 :(得分:0)
如果受网络带宽限制,则需要相同的时间。如果您从同一站点下载所有10个文件,那么它将不会更快。当您希望用户界面响应或者您拥有处理器密集型和多核心
时,多线程非常有用答案 2 :(得分:0)
WaitHandle[] handles = new WaitHandle[5];
handles[0] = d.BeginInvoke(downloadFileComplete, d).AsyncWaitHandle;
handles[1] = d.BeginInvoke(downloadFileComplete, d).AsyncWaitHandle;
handles[2] = d.BeginInvoke(downloadFileComplete, d).AsyncWaitHandle;
handles[3] = d.BeginInvoke(downloadFileComplete, d).AsyncWaitHandle;
handles[4] = d.BeginInvoke(downloadFileComplete, d).AsyncWaitHandle;
WaitHandle.WaitAll(handles);
答案 3 :(得分:0)
此实现中有许多内容对于并发使用是不安全的。
但可能导致您描述的效果的是downloadFile()
有一个while()
循环来检查scanning
变量。此变量由正在运行的委托的所有实例共享。 while while循环阻止代理同时运行。
这个“扫描循环”不是一个合适的线程构造 - 两个线程都可以同时读取变量并同时将它设置为 。您应该使用Semaphore
或lock()
语句来保护DataTable免受并发访问。由于每个线程花费大部分时间等待scanning
为假,因此downloadFile
方法不能同时运行。
您应该重新考虑如何构建此代码,以便下载数据和更新应用程序中的结构不会出现争用。
答案 4 :(得分:0)
我认为这样的事情会更好。
public class PhotoDownload
{
public ManualResetEvent Complete { get; private set; }
public Object RequireData { get; private set; }
public Object Result { get; private set; }
}
public void DownloadPhotos()
{
var photos = new List<PhotoDownload>();
// build photo download list
foreach (var photo in photos)
{
ThreadPool.QueueUserWorkItem(DownloadPhoto, photo);
}
// wait for the downloads to complete
foreach (var photo in photos)
{
photo.Complete.WaitOne();
}
// make sure everything happened correctly
}
public void DownloadPhoto(object state)
{
var photo = state as PhotoDownload;
try
{
// do not access fields in this class
// everything should be inside the photo object
}
finally
{
photo.Complete.Set();
}
}