2015-09-16 3 views
0

Я хочу отобразить некоторые данные о загрузке формы в gridview, данные, которые я хочу отображать, находятся в большом количестве строк, когда я использую фоновый рабочий процессор, он показывает мне следующую ошибку ,Работа с перекрестными потоками недействительна в BackgroundWorker

Мой код:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
{ 
    try 
    { 
     FTPUtility obj = new FTPUtility(); 
     dataGridViewRequest.DataSource = obj.ListRequestFiles(); 
     dataGridViewRequest.Columns[0].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; 
     dataGridViewRequest.Columns[1].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; 
     dataGridViewRequest.Columns[2].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; 

     dataGridViewResponses.DataSource = obj.ListResponseFiles(); 
     dataGridViewResponses.Columns[0].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; 
     dataGridViewResponses.Columns[1].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; 
     dataGridViewResponses.Columns[2].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; 

    } 
    catch (Exception ex) 
    { 

     MessageBox.Show(ex.Message, "Error Message", MessageBoxButtons.OK, MessageBoxIcon.Error); 
    } 
} 

Форма нагрузка:

private void FormFTP_Load(object sender, EventArgs e) 
{ 
    try 
    { 

     //this.comboBoxRequests.SelectedIndex = 0; 
     backgroundWorker1.RunWorkerAsync(); 

    } 
    catch (Exception ex) 
    { 

     MessageBox.Show(ex.Message, "Error Message", MessageBoxButtons.OK, MessageBoxIcon.Error); 
    } 
} 
+0

Что вы делаете (= попытка изменить элемент GUI из потока BackgroundWorker) не может быть выполнена. Вам лучше прочитать что-нибудь о «BackgroundWorker»/многопоточности в C# /. NET, потому что ваши идеи не совсем понятны. – varocarbas

+0

@varocarbas Я пытаюсь получить файлы из ftp на моем локальном приложении Windows datagridview, когда я загружаю данные, требуется слишком много времени, поэтому я хочу сначала загрузить основную форму, а затем через секунду файлы будут отображаться в datagridview. –

+0

Как объяснялось в моем предыдущем комментарии: ваш код делает что-то, что доказывает вам абсолютную нехватку знаний относительно «BackgroundWorker» и основных идей (т. Е. Многопоточности). Улучшите свои знания на обоих фронтах, вместо того, чтобы делать слепые тесты и жаловаться на каждую отдельную проблему, которую вы видите. – varocarbas

ответ

3

Есть много различных способов, чтобы предотвратить форму от быть заморожены.
Например, вы можете загрузить свои данные, как это:

private async void Form_Load(object sender, EventArgs e) 
{ 
    //do some initializations 
    await LoadData(); 
    //do some other initializations that you need to perform. 
} 

private async Task LoadData() 
{ 
    //Load your data here 
    //For example 
    FTPUtility obj = new FTPUtility(); 
    dataGridViewRequest.DataSource = obj.ListRequestFiles(); 
} 

Этот способ при запуске формы, команды выполняются в последовательности, вы писали в то время как интерфейс реагирует, и вы не будете сталкиваться с общими трудностями использования BackgroundWorker или потоки, такие как исключения перекрестной резьбы.

Ключевым моментом является использование async/wait. Для более подробной информации читайте Asynchronous Programming with Async and Await

Помните, что таким образом, каждый, где вы хотите позвонить LoadData, вы должны назвать это таким образом:

await LoadData(); 

И метод, который вы пишете этот код, должен быть асинхронной:

private async void RefreshButton_Click(object sender, EventArgs e) 
{ 
    await LoadData(); 
} 

EDIT для .Net 4.0


Для .Net 4.0 вы можете использовать как Task.Run, так и BackgroundWorker. Я рекомендую Task.Run, потому что он более прост и читаем.

Обратите внимание, что операция поперечной резьбы исключается при доступе к элементам пользовательского интерфейса из другого потока, кроме UI. В таких ситуациях вместо этого вы должны использовать this.Invoke(new Action(()=>{/*Access UI Here*/}));. И никогда не ставьте трудоемкую задачу в свою часть invoke.

BackgroundWorker подход:

private void Form1_Load(object sender, EventArgs e) 
{ 
    backgroundWorker1.RunWorkerAsync(); 
    //If you put some code here for example MessageBox.Show(""); 
    //The code will immadiately run and don't wait for worker to complete the task. 
} 

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
{ 
    this.LoadData(); 
} 

public void LoadData() 
{ 
    var ftp = new FtpUtility(); 
    var data = ftp.ListRequestFiles(); 
    this.Invoke(new Action(() => 
    { 
     //Setup columns and other UI stuff 
     //Set datasource of grid 
     this.dataGridView1.DataSource = data; 
    })); 
} 
  • Помните везде вы раньше использовали LoadData, теперь вы должны использовать backgroundWorker1.RunWorkerAsync(); вместо этого.
  • Если вы хотите выполнить работу после того, как работник выполнит задачу, поместите свое задание в backgroundWorker1_RunWorkerCompleted или в Invoke часть LoadData.

Task.Run подход

private void Form1_Load(object sender, EventArgs e) 
{ 
    Task.Run(() => 
    { 
     LoadData(); 
    }) 
    .ContinueWith(x => 
    { 
     //You can put codes you want run after LoadData completed here 
     //If you access the UI here, you should use Invoke 
    }); 
    //If you put some code here for example MessageBox.Show(""); 
    //The code will immadiately run and don't wait for worker to complete the task. 
} 

public void LoadData() 
{ 
    var ftp = new FtpUtility(); 
    var data = ftp.ListRequestFiles(); 
    this.Invoke(new Action(() => 
    { 
     //Setup columns and other UI stuff 
     //Set datasource of grid 
     this.dataGridView1.DataSource = data; 
    })); 
} 
  • Помните везде вы раньше использовали LoadData, теперь вы должны использовать Task.Run(()=>{LoadData();}); вместо этого.
  • Если вы хотите сделать работу после завершения LoadData, оставьте свою работу в ContinueWith или в Invoke часть LoadData.
+0

private async его дать ошибку, вы пропускаете пространство имен. –

+0

@RazimKhan Это работает для .Net 4.5 и кажется, что вы работаете с .Net 4.0, поэтому я обновлю код .Net 4.0 –

+0

@RazimKhan Я отредактировал ответ и предложил 2 способа, надеюсь, что вы найдете его полезным :) –

1

Попробуйте это.

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
{ 
    try 
    { 

     dataGridViewRequest.Invoke(new Action(() => { 
      FTPUtility obj = new FTPUtility(); 
      dataGridViewRequest.DataSource = obj.ListRequestFiles(); 
      dataGridViewRequest.Columns[0].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; 
      dataGridViewRequest.Columns[1].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; 
      dataGridViewRequest.Columns[2].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; 

      dataGridViewResponses.DataSource = obj.ListResponseFiles(); 
      dataGridViewResponses.Columns[0].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; 
      dataGridViewResponses.Columns[1].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; 
      dataGridViewResponses.Columns[2].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; 
     })); 
    } 
    catch (Exception ex) 
    { 

     MessageBox.Show(ex.Message, "Error Message", MessageBoxButtons.OK, MessageBoxIcon.Error); 
    } 
} 

private void FormFTP_Load(object sender, EventArgs e) 
{ 
    try 
    { 

     //this.comboBoxRequests.SelectedIndex = 0; 
     backgroundWorker1.RunWorkerAsync(); 

    } 
    catch (Exception ex) 
    { 

     MessageBox.Show(ex.Message, "Error Message", MessageBoxButtons.OK, MessageBoxIcon.Error); 
    } 
} 
+0

Никогда не пытайтесь это;) вы ставите трудоемкую задачу в 'Invoke', что она означает, что она будет работать в потоке пользовательского интерфейса, и она замерзнет UI снова. Вместо этого вы должны запускать трудоемкую задачу сначала, а затем в invoke part, только получить доступ к пользовательскому интерфейсу и установить свойства пользовательского интерфейса, в этом примере 'obj.ListRequestFiles();' нужно вызывать перед вызовом и никогда не вызывать часть. Чтобы проверить это, вы должны использовать 'Thread.Sleep (5000)' вместо этого 'obj.ListRequestFiles();', чтобы увидеть результат. –

+0

Это не затормозит пользовательский интерфейс, потому что вы используете фонового рабочего :) – dan

+0

Общая ошибка в 'BackgroundWorker', да, она работает в другом потоке, но когда вы используете' Invoke', код в действии, который передается для вызова, будет запущен в пользовательском интерфейсе нить, и поскольку в этом действии вы закладываете основную временную замерзающую строку кода, пользовательский интерфейс будет заморожен. Чтобы проверить это, вы должны использовать 'Thread.Sleep (5000)' вместо этого 'obj.ListRequestFiles();', чтобы увидеть результат. –