C# 在C中异步运行多个委托并等待响应#
我有一个URL表,需要循环浏览,下载每个文件,更新表并返回结果。我希望一次最多下载10次,因此我考虑按如下方式使用代理:C# 在C中异步运行多个委托并等待响应#,c#,delegates,C#,Delegates,我有一个URL表,需要循环浏览,下载每个文件,更新表并返回结果。我希望一次最多下载10次,因此我考虑按如下方式使用代理: DataTable photos; bool scanning = false, complete = false; int rowCount = 0; public delegate int downloadFileDelegate(); public void page_load(){ photos = Database.getData...
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);
}
DataTable照片;
布尔扫描=假,
完整=错误;
int rowCount=0;
public delegate int downloadFileDelegate();
公共无效页面加载(){
照片=数据库。获取数据。。。
downloadFileDelegate d=下载文件;
d、 BeginInvoke(下载文件完成,d);
d、 BeginInvoke(下载文件完成,d);
d、 BeginInvoke(下载文件完成,d);
d、 BeginInvoke(下载文件完成,d);
d、 BeginInvoke(下载文件完成,d);
而(!complete){}
//处理结果。。。
}
int下载文件(){
while(scanning){}scanning=true;
数据行r;
对于(int ii=0;ii
但是,当我运行这个程序时,运行5所花费的时间与运行1所花费的时间相同。我原以为要快5倍
有什么想法吗?如果您受到网络带宽的限制,则需要相同的时间。如果您从同一个站点下载所有10个文件,那么不会更快。当您希望用户界面做出响应,或者您有处理器密集型和多核设备时,多线程非常有用您看起来像是在尝试使用无锁同步(使用
while(scanning)
检查在函数开头设置并在结尾重置的布尔值),但所有这些成功的做法都是一次只运行一次检索
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);
扫描
标志(以及相关逻辑)volatile
(否则,它们的读取可能会被缓存,您可以无休止地等待)DataRow
中的值)必须在UI线程上进行。您必须将这些操作包装在控件中。Invoke
或控件。BeginInvoke
调用,否则您将跨线程边界与控件交互BeginInvoke
返回一个AsyncWaitHandle
。当操作全部完成时,将使用此逻辑。像这样的这将导致调用线程阻塞,直到所有操作完成。此实现中有许多东西对于并发使用是不安全的 但可能导致您描述的效果的原因是
downloadFile()
有一个while()
循环,用于检查扫描
变量。此变量由正在运行的委托的所有实例共享。此while循环防止代理并发运行
这个“扫描循环”不是一个正确的线程构造-两个线程可能同时读取变量和设置变量。您应该真正使用信号量
或lock()
语句来保护数据表不受并发访问。由于每个线程大部分时间都在等待扫描
为false,因此downloadFile
方法不能同时运行
您应该重新考虑如何构造此代码,以便在应用程序中下载数据和更新结构不会引起争议。我认为这样做会更好
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();
}
}
公共类光下载
{
公共手动重置事件完成{get;private set;}
所需的公共对象数据{get;private set;}
公共对象结果{get;private set;}
}
公开作废下载照片()
{
var photos=新列表();
//创建照片下载列表
foreach(照片中的var照片)
{
QueueUserWorkItem(下载照片,照片);
}
//等待下载完成
foreach(照片中的var照片)
{
photo.Complete.WaitOne();
}
//确保一切都正确发生
}
公共void下载照片(对象状态)
{
var photo=状态为PhotoDownload;
尝试
{
//不要访问此类中的字段
//一切都应该在照片对象内
}
最后
{
photo.Complete.Set();
}
}
这是一种更好的技术,但无助于提高性能。这并不能解决实际导致代码执行错误的问题,而不是对共享数据的不正确并发访问。在某些情况下,延迟最终会限制单个连接的吞吐量,多次下载可能会更快一些。我同意Yuliy的观点-我将“不会更快”改为“可能不会更快”,作为一个次要问题,你的代码不是线程安全的。是的,代码似乎试图序列化下载操作。我要指出,你指的是while(扫描)而不是while(!complete),正如最初我对你所指的感到困惑
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();
}
}