กำลังส่งอาร์กิวเมนต์ไปที่ Background Worker หรือไม่


147

สมมติว่าฉันต้องการส่งพารามิเตอร์ int ไปยังผู้ทำงานเบื้องหลัง, สิ่งนี้จะสำเร็จได้อย่างไร?

private void worker_DoWork(object sender, DoWorkEventArgs e) {

}

ฉันรู้ว่าเมื่อใดที่นี่คือ worker.RunWorkerAsync (); ฉันไม่เข้าใจวิธีการกำหนดใน worker_DoWork ว่าควรใช้พารามิเตอร์ int

คำตอบ:


235

คุณเริ่มต้นเช่นนี้:

int value = 123;
bgw1.RunWorkerAsync(argument: value);  // the int will be boxed

แล้ว

private void worker_DoWork(object sender, DoWorkEventArgs e) 
{
   int value = (int) e.Argument;   // the 'argument' parameter resurfaces here

   ...

   // and to transport a result back to the main thread
   double result = 0.1 * value;
   e.Result = result;
}


// the Completed handler should follow this pattern 
// for Error and (optionally) Cancellation handling
private void worker_Completed(object sender, RunWorkerCompletedEventArgs e) 
{
  // check error, check cancel, then use result
  if (e.Error != null)
  {
     // handle the error
  }
  else if (e.Cancelled)
  {
     // handle cancellation
  }
  else
  {          
      double result = (double) e.Result;
      // use it on the UI thread
  }
  // general cleanup code, runs when there was an error or not.
}

38
ฉันจะทำข้อโต้แย้งสองข้อได้อย่างไร
sooprise

3
หรือฉันจะส่งวัตถุที่มีอาร์กิวเมนต์มากกว่าหนึ่งอัน?
sooprise

23
@soo: ใช้คลาสผู้ช่วยหรือTuple<A,B>(C # 4 +) (แก้ไข: ใช่ใช้วัตถุเพื่อแพ็คมันทั้งหมดเข้าดูตัวอย่าง DoWorkEventArgs ด้วยตนเอง
Henk Holterman

แต่คุณจะแจ้ง UI ของผลลัพธ์ได้อย่างไร
rayray

1
@rayray: label1.Text = e.Result.ToString();ทุกที่ที่ฉันทำเครื่องหมายว่าปลอดภัย
Henk Holterman

101

แม้ว่านี่จะเป็นคำถามที่ตอบแล้ว แต่ฉันก็ยังมีทางเลือกอื่นที่ IMO อ่านง่ายกว่ามาก:

BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += (obj, e) => WorkerDoWork(value, text);
worker.RunWorkerAsync();

และวิธีการจัดการ:

private void WorkerDoWork(int value, string text) {
    ...
}

12
ฉันไม่รู้ว่า IMO หมายถึงอะไรฉันคิดว่ามันเป็น C # ฉัน googled "C # IMO" และลงจอดที่นี่และได้รับคำตอบ ... lol quantnet.com/threads/cc-vba-or-java.11433
electricalbah

พารามิเตอร์ประมาณ 3 ตัว?
YukiSakura

ฉันไม่ได้เล่นกับ. NET ตั้งแต่ปี 2012 แต่ถ้าฉันไม่เข้าใจผิดคุณสามารถเพิ่มพารามิเตอร์ที่คุณต้องการ ... => WorkerDoWork(a, b, c);ตราบใดที่มันตรงกับลายเซ็นของวิธีการ... WorkerDoWork(int a, string b, string c) {...
dcarneiro

1
โปรดจำไว้ว่าถ้าคุณใช้สิ่งนี้ (เช่นที่ฉันพยายามจะทำ) คุณต้องสร้าง backgroundworker ใหม่ทุกครั้ง (ในตัวอย่างที่คุณทำ) มิฉะนั้นคุณจะมีปัญหาเหมือนฉัน ผู้ทำงานเบื้องหลังของฉันจะทำซ้ำการทำงานก่อนหน้านี้อย่างต่อเนื่อง ถ้าวิ่งครั้งเดียวก็ไม่เป็นไร 2 ครั้งมันซ้ำแล้วซ้ำอีกครั้งล่าสุดและการทำงานปัจจุบัน การวิ่งครั้งที่ 3 จะทำซ้ำสองครั้งล่าสุดและปัจจุบัน ฯลฯ
bshea

แต่ค่าจะถูกส่งผ่านไปยัง RunWorkerAsync อย่างไร
CodyBugstein

47

คุณสามารถส่งผ่านข้อโต้แย้งหลายอย่างเช่นนี้ได้

List<object> arguments = new List<object>();
                    arguments.Add(argument 1);
                    arguments.Add(argument 1);
                    arguments.Add(argument n);


                    backgroundWorker2.RunWorkerAsync(arguments);

private void worker_DoWork(object sender, DoWorkEventArgs e) {

  List<object> genericlist = e.Argument as List<object>;
  extract your multiple arguments from this list and cast them and use them. 

}

@missReclusive โยนรายการ "genericlist" กล่าวคือสมมติว่า "อาร์กิวเมนต์ 1" เป็นประเภท int แล้ว int อาร์กิวเมนต์1 = (int) รายการทั่วไป [0]
Zain Ali

1
นี่เป็นความคิดที่ไม่ดีในแง่ของการบำรุงรักษา คุณควรใช้รูปแบบที่เป็นรูปธรรมเหนือ List <object> เพราะอย่างน้อยคุณจะสามารถคิดได้ว่าคุณกำลังทำอะไรอยู่ (ดูตัวอย่างในคำตอบของฉันด้านล่าง)
Denis

ฉันอาจจะชอบTuple(หรือคลาสพิเศษ) มากกว่ารายการของวัตถุทั่วไป
James S


6

ตรวจสอบคุณสมบัติ DoWorkEventArgs.Argument :

...
backgroundWorker1.RunWorkerAsync(yourInt);
...

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    // Do not access the form's BackgroundWorker reference directly.
    // Instead, use the reference provided by the sender parameter.
    BackgroundWorker bw = sender as BackgroundWorker;

    // Extract the argument.
    int arg = (int)e.Argument;

    // Start the time-consuming operation.
    e.Result = TimeConsumingOperation(bw, arg);

    // If the operation was canceled by the user, 
    // set the DoWorkEventArgs.Cancel property to true.
    if (bw.CancellationPending)
    {
        e.Cancel = true;
    }
}

5

คุณสามารถลองใช้งานได้หากคุณต้องการส่งอาร์กิวเมนต์มากกว่าหนึ่งประเภทก่อนอื่นให้เพิ่มอาร์กิวเมนต์ทั้งหมดลงในอาร์เรย์ประเภทวัตถุและส่งผ่านวัตถุนั้นไปยัง RunWorkerAsync () ที่นี่เป็นตัวอย่าง:

   some_Method(){
   List<string> excludeList = new List<string>(); // list of strings
   string newPath ="some path";  // normal string
   Object[] args = {newPath,excludeList };
            backgroundAnalyzer.RunWorkerAsync(args);
      }

ขณะนี้อยู่ในวิธีการทำงานของคนทำงานพื้นหลัง

backgroundAnalyzer_DoWork(object sender, DoWorkEventArgs e)
      {
        backgroundAnalyzer.ReportProgress(50);
        Object[] arg = e.Argument as Object[];
        string path= (string)arg[0];
        List<string> lst = (List<string>) arg[1];
        .......
        // do something......
        //.....
       }

2
+1 การส่งข้อโต้แย้งในลักษณะนี้ยังช่วยหลีกเลี่ยงการเริ่มตัวทำงานเบื้องหลังใหม่ในการทำงานแต่ละครั้งเพื่อหลีกเลี่ยงการทำซ้ำ (อย่างน้อยในแอพของฉัน) ดูความคิดเห็นของฉันด้านล่างที่เกี่ยวข้องกับปัญหานี้ ยังstackoverflow.com/a/12231431/503621 & stackoverflow.com/questions/12507602/ …
bshea


4

คุณควรพยายามใช้วัตถุคอมโพสิตที่มีประเภทคอนกรีต (ใช้รูปแบบการออกแบบคอมโพสิต) มากกว่ารายการประเภทวัตถุ ใครจะจำได้ว่าสิ่งเหล่านี้คืออะไร ลองนึกถึงการบำรุงรักษารหัสของคุณในภายหลัง ... ลองทำสิ่งนี้แทน:

Public (Class or Structure) MyPerson
                public string FirstName { get; set; }
                public string LastName { get; set; }
                public string Address { get; set; }
                public int ZipCode { get; set; }
End Class

แล้ว:

Dim person as new MyPerson With { .FirstName = Joe”,
                                  .LastName = "Smith”,
                                  ...
                                 }
backgroundWorker1.RunWorkerAsync(person)

แล้ว:

private void backgroundWorker1_DoWork (object sender, DoWorkEventArgs e)
{
        MyPerson person = e.Argument as MyPerson
        string firstname = person.FirstName;
        string lastname = person.LastName;
        int zipcode = person.ZipCode;                                 
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.