2009-10-20 6 views
0

내 Windows 양식에서 "tb_LogBox"텍스트 상자가 텍스트 상자와 단추가 있는데, 함수를 호출해야하는 백그라운드 작업자를 만들려고합니다. 즉 " LogTimer.DnT() "컴파일 할 때 Visual Studio에서 InvalidOperationException을 throw합니다.C# InvalidOperationException 및 크로스 스레드 작업

실제로 오류가 발생합니다 크로스 스레드 작업이 유효하지 않습니다 : 생성 된 스레드가 아닌 다른 스레드에서 액세스 된 컨트롤 'tb_LogBox'가 유효합니다. 다음 샘플 코드는 I 당신은 UI 스레드에서 컨트롤의 메소드를 호출 할 필요가

private void button1_Click(object sender, EventArgs e) 
{ 
    try 
    { 
     var bw = new BackgroundWorker(); 
     bw.DoWork += ExecuteOperations ; 
     bw.RunWorkerAsync(); 
    } 
    catch (Exception ex) 
    { 
     tb_LogBox.AppendText(Environment.NewLine + " [email protected]= " + ex.Message+" "+ex.Source); 
    } 
} 

private void ExecuteOperations(object sender, DoWorkEventArgs e) 
{ 
    var FuncCall = new LogTimer(); 
    tb_LogBox.AppendText(Environment.NewLine + FuncCall.DnT()); // the line i am getting the error. on 
} 

public class LogTimer 
{ 
    public string DnT() 
    { 
     const string datePat = @"d/MM/yyyy"; 
     var dateTime = DateTime.Now(); 
     return dateTime.ToString(datePat); 
    } 
} 

답변

4

은 시작 호출 방법을 사용해보십시오.

0

을하려고하고 무엇을 보여

private void ExecuteOperations(object sender, DoWorkEventArgs e) 
{ 
    var FuncCall = new LogTimer(); 
    tb_LogBox.Invoke((MethodInvoker)delegate{ 
     tb_LogBox.AppendText(Environment.NewLine + FuncCall.DatenTime()); 
    }); 
} 

내가 LogTimer가 무엇 모르겠지만, 그것은 매우 수도 물론 당신은뿐만 아니라 대리인 안에 그것을 만들 것을 수 :

private void ExecuteOperations(object sender, DoWorkEventArgs e) 
{ 
    tb_LogBox.Invoke((MethodInvoker)delegate{ 
     var FuncCall = new LogTimer(); 
     tb_LogBox.AppendText(Environment.NewLine + FuncCall.DatenTime()); 
    }); 
} 
1

이 ExecuteOperations에서이 작업을 수행 :

tb_LogBox.Invoke((MethodInvoker)delegate() { tb_LogBox.AppendText(...) })); 

UI 구성 요소를 변경하기 위해 BackgroundWorker에서 .NET 스레드 풀 스레드를 사용하는 다른 스레드를 사용할 수 없습니다. 이것은 WinForms 프로그래밍에 익숙해 져야 할 주요한 장애물입니다.

2

Ui 변경 사항을 UI 스레드에 정렬해야합니다. 이것은 윈폼 응용 프로그램에 tb_LogBox.AppendText

주위 호출/begininvoke 호출을 사용하여 수행 할 수 있습니다 WPF 응용 프로그램에서

this.BeginInvoke((MethodInvoker)delegate 
     { 
      tb_LogBox.AppendText(Environment.NewLine + FuncCall.DatenTime()); 
     }); 

을 :

this.Dispatcher.BeginInvoke(
     (Action)delegate() 
     { 
      tb_LogBox.AppendText(Environment.NewLine + FuncCall.DatenTime()); 
     }); 

희망이 도움이!

BeginInvoke(new Action(() => 
    { 
     tb_LogBox.AppendText(Environment.NewLine + FuncCall.DnT()); 
    })); 

이 호출보다 더 부드러운 것 :

0

BackgroundWorker는 자체 스레드에서 실행되며 WinForms GUI 요소와 관련된 모든 작업은 작성된 스레드에서 실행해야합니다. 현재 BackgroundWorker를 사용하는 방법은 ThreadPool.QueueUserWorkItem()을 사용하여 작업을 큐에 넣는 것과 동일합니다. BackgroundWorker를 사용하여 GUI로 다시 통신하려면 ReportProgess를 사용하거나 작업자 메서드에서 DoWorkEventArgs.Result 속성을 설정하고 GUI 스레드에서 해당 이벤트에 반응하십시오. WinForms 컨트롤에서 Invoke/BeginInvoke를 사용하여 GUI 스레드에서 직접 임의의 코드를 실행할 수도 있습니다. 귀하의 경우에는 tb_LogBox에 액세스하는 줄을 다음과 같이 바꿔야합니다.

tb_LogBox.Invoke(new Action(() => 
    tb_LogBox.AppendText(Environment.NewLine + FuncCall.DatenTime()); 
)); 
0

백그라운드 작업자의 실행 스레드에서 호스트 스레드에 액세스 할 수 없습니다. BackgroundWorker의 ReportProgress 메서드를 사용하여 정보를 호스트 스레드로 보낼 수 있습니다.

private void button1_Click(object sender, EventArgs e) 
{ 
    try 
    { 
     var bw = new BackgroundWorker(); 
     bw.DoWork += ExecuteOperations; 
     bw.ProgressChanged += bw_ProgressChanged; 
     bw.RunWorkerAsync(); 
    } 
    catch (Exception ex) 
    { 
     tb_LogBox.AppendText(Environment.NewLine + " [email protected]= " + ex.Message + " " + ex.Source); 
    } 
} 

private static void ExecuteOperations(object sender, DoWorkEventArgs e) 
{ 
    var FuncCall = new LogTimer(); 
    string text = Environment.NewLine + FuncCall.DnT(); 

    (sender as BackgroundWorker).ReportProgress(0, text); 
} 
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e) 
{ 
    tb_LogBox.AppendText(e.UserState as string); 
} 

public class LogTimer 
{ 
    public string DnT() 
    { 
     const string datePat = @"d/MM/yyyy"; 
     var dateTime = DateTime.Now; 
     return dateTime.ToString(datePat); 
    } 
}