DownloadDataAsync + مندوب C # يعمل في main ولكن ليس في وظيفة من الدرجة
سؤال
وركضت هذا الرمز الأشخاص. عملت كرئيسي ولكن عندما وضعه في صفي كان لا يعمل. لماذا ا؟ كيفية استخدام WebClient.DownloadDataAsync ( ) طريقة في هذا السياق؟
ووتحطم البيانات قبل الميلاد == لاغيا وباطلا يطرح استثناء: (
.public class Test2
{
public void func()
{
byte[] data = null;
WebClient client = new WebClient();
client.DownloadDataCompleted +=
delegate(object sender, DownloadDataCompletedEventArgs e)
{
data = e.Result;
};
Console.WriteLine("starting...");
client.DownloadDataAsync(new Uri("https://stackoverflow.com/questions/"));
while (client.IsBusy)
{
Console.WriteLine("\twaiting...");
Thread.Sleep(100);
}
Console.WriteLine("done. {0} bytes received;", data.Length);
}
}
//i tried calling on form_load and a button click
new Test2().func();
المحلول
وهذا الرمز لديه على حقل البيانات. ودعا مندوب مجهول DownloadDataCompleted من موضوع مختلف عن إجراء استدعاء data.Length وعند نقطة التي يتم استدعاء DownloadDataCompleted وIsBusy يصبح كاذبة. انه سباق بين المواضيع اثنين على الذين يصلون إلى البيانات أولا. إذا استدعت موضوع الرئيسي data.Length قبل أن يتم تحديد البيانات على موضوع حمل تحصل استثناء مرجع فارغة الخاص بك. فمن السهل أن نرى لا بد منه إذا كنت إجبار DownloadDataCompleted حذف فضفاضة دائما السباق أضاف Thread.Sleep () الدعوة إليها قبل أن يحدد البيانات.
وسوف الدول والترابط تبدو هذه:
Main Thread Download Thread client.IsBusy
Waiting.... downloading... true
leaves waiting loop calls delegate false
calls data.Length data = e.Result
وليس هناك طريقة لمعرفة أي موضوع سيتم تشغيل السطر الأخير أولا. على جهاز معالج متعددة، سواء تلك يمكن أن تعمل في وقت واحد.
وبما أن هذا هو أساس كل شيء على توقيت أحيانا أنه سوف يعمل وبعض الأحيان ستفشل. كنت بحاجة الى بعض نوع من التزامن (قفل) على جميع البيانات التي يتم الوصول إليها عن المواضيع موتليبلي.
نصائح أخرى
ونظرا لنموذج خيوط من winform (كما أشار shf301
الخروج)، لقد تعديل القوانين التي تعمل بالنسبة لي.
private void button1_Click(object sender, EventArgs e)
{
func();
}
void func()
{
WebClient client = new WebClient();
byte[] data = null;
long rcv = 0; //last number of bytes received
//check data received for progress
client.DownloadProgressChanged += delegate(object sender, DownloadProgressChangedEventArgs e)
{
if (e.BytesReceived - rcv > 1000)
{
Console.WriteLine("\tBytes Received: " + e.BytesReceived.ToString());
rcv = e.BytesReceived;
}
//else don't report
Thread.Sleep(1);
};
client.DownloadDataCompleted +=
delegate(object sender, DownloadDataCompletedEventArgs e)
{
data = e.Result;
Console.WriteLine("done. {0} bytes received;", data.Length);
};
Console.WriteLine("starting...");
//fire and forget
client.DownloadDataAsync(new Uri("http://stackoverflow.com/questions/"));
}
وهناك إخراج:
starting...
Bytes Received: 8192
Bytes Received: 11944
Bytes Received: 15696
Bytes Received: 20136
Bytes Received: 24232
Bytes Received: 28040
Bytes Received: 32424
Bytes Received: 36176
Bytes Received: 40616
Bytes Received: 44712
Bytes Received: 48269
done. 48269 bytes received;
وكان يعمل للي؟
C:\TEMP\ConsoleApplication5\bin\Debug>ConsoleApplication5.exe
starting...
waiting...
waiting...
waiting...
waiting...
waiting...
waiting...
waiting...
waiting...
waiting...
waiting...
waiting...
waiting...
waiting...
waiting...
waiting...
waiting...
waiting...
waiting...
waiting...
waiting...
waiting...
waiting...
waiting...
waiting...
waiting...
waiting...
done. 48178 bytes received;
ما هي الفائدة من استخدام الأسلوب المتزامن إذا كنت انتظر النتيجة في الحلقة؟ مجرد استخدام النسخة متزامن:
public class Test2
{
public void func()
{
WebClient client = new WebClient();
byte[] data = client.DownloadData(new Uri("http://stackoverflow.com/questions/"));
Console.WriteLine("done. {0} bytes received;", data.Length);
}
}
//i tried calling on form_load and a button click
new Test2().func();