2010-01-15 12 views
12

UPDATE를 표시합니다 : 그냥 내 질문에 아래로 삶은 것을 요약 :가능한 다음, 백그라운드 스레드에서 양식을 구성 UI 스레드에서

내가 건설 .NET 폼과 컨트롤은 모든 창 핸들을 만들지 않은 것을 기대했다 - - Form.Show /Form.ShowDialog

까지 프로세스가 지연되기를 기대하는 사람이 있는지를 확인하거나 거부 할 수 있습니까?


두드러진 WinForms 폼에는 탭 컨트롤이 있습니다. 폼에있는 많은 컨트롤이 몇 초 동안로드되는 동안 일시 중지됩니다. 필자는 생성자 또는 OnLoad에서 내 논리가 아닌 InitializeComponent에서 디자이너가 생성 한 코드로 범위를 좁혔습니다.

주 UI 스레드가 아닌 다른 스레드에서 UI와 상호 작용할 수 없다는 것을 잘 알고 있지만 응용 프로그램에이 양식을 미리로드해야합니다. 백그라운드에서 생성자를 실행하십시오. 따라서 사용자가 UI 스레드를 열려고하는 즉시 UI 스레드에 표시 할 준비가되었습니다. 그러나, 디자이너에서이 줄에, 백그라운드 스레드에서 건설 :

this.cmbComboBox.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.Suggest; 

나는 오류

납니다

현재 스레드가 단일 스레드 아파트 (STA) 모드 이전으로 설정해야합니다 OLE 전화를 걸 수 있습니다. 주 기능에 STAThreadAttribute 이 표시되어 있는지 확인하십시오.

지금이 날 일반적으로이 전략이 작동합니다 희망을주는 디자이너 파일, 중간 아래입니다. 그러나이 특정 라인은 일종의 OLE 호출을 즉시 시작하려고 시도하는 것 같습니다.

아이디어가 있으십니까?

편집 :

나는 여기에 자신을 분명히하지 않는다고 생각합니다. 설계자가 생성 한 코드에서 bazillion 컨트롤을 생성하는 동안 딜레이가 발생하는 것으로 보입니다.

실제로이 형식이 실제로 표시되지 않았기 때문에이 초기화 코드가 실제로 실제 Win32 창 개체를 건드리지 않고 일어났습니다.

이 배경 스레드에서 레이블 텍스트와 위치를 설정할 수 있다는 사실이 나에게 사실이라는 희망을주었습니다. 그러나 그것은 모든 속성에 대해 사실이 아닐 수도 있습니다.

+0

예외는 cmbox 또는 모든 컨트롤에서만 발생합니다. 그렇다면 콤보 상자에서 AutoCompleteMode를 설정하기 위해 마지막으로 –

+0

으로 속성을 설정할 수 있기 때문입니다. 위의 디자이너는 텍스트/이름/위치/크기/등을 설정하는 코드가 많습니다. 컨트롤의 속성 – Clyde

+0

양식을 '미리로드'하는 동안 응용 프로그램은 무엇을 할 것입니까? '기다려주세요'라는 메시지가 표시 될 수 있습니까? – Codesleuth

답변

3

대답은 no입니다.

GUI 스레드가 아닌 다른 스레드에서 창 핸들을 만들면 결코 표시 할 수 없습니다.

편집 : Forms 및 컨트롤을 만들고 주 GUI 스레드가 아닌 다른 스레드에서 을 완전히 표시 할 수 있습니다. 물론 을 실행하면 생성 한 스레드 에서 멀티 스레드 GUI에만 액세스 할 수 있지만 가능합니다. - 애슐리 헨더슨

당신은 BG 스레드에서 무거운 리프팅을 수행 한 다음에 데이터를로드 GUI 위젯을

+0

.NET 컨트롤의 생성자가 실제로 창 핸들을 생성한다는 것을 확인할 수 있습니까? 그것에 관한 문서가 있습니까? – Clyde

+0

절대 신경 쓰지 마라. 나는 Handle이 건설 후 채워지는 것을 본다. – Clyde

+4

@Jan, 올바르지 않습니다. 폼과 컨트롤을 만들고 GUI GUI 스레드가 아닌 다른 스레드에 표시 할 수 있습니다. 물론 이렇게하면 다중 쓰레드 GUI를 만들었던 쓰레드에서 접근 할 수 있지만 가능합니다. – Ash

1

일반적으로 양식의 속성은 메시지 루프를 실행하는 동일한 스레드에서 액세스해야합니다. 즉, 다른 스레드에서 양식을 구성하려면 BeginInvoke를 사용하여 실제로 속성을 설정하기 위해 호출을 마샬링해야합니다. 생성자가 처리해야하는 메시지를 생성하는 경우 (현재 사용자에게 발생하는 것처럼) 생성자의 속성 집합에도 해당됩니다.

작업을 시작한다고해도 무엇을 사나요? 전반적으로 조금 더 느리고 빠르지는 않을 것입니다.

이 양식을로드하는 동안 스플래시 화면을 표시 하시겠습니까?

또는 양식을 작성하는 데 너무 오래 걸리는 이유를 검토하십시오. 이 작업은 몇 초가 걸리는 것이 일반적이지 않습니다.

+0

이것은 나에게 가장 이해가됩니다. 이 작업을 수행하는 방법에 대한 샘플은 MethodInvoker 대리자를 확인하십시오. bg 스레드는 leg 작업을 수행하는 반면 GUI 스레드는 여전히 독립적으로 실행되어 사용자 입력을받습니다. – spoulson

4

약간의 이해로 생각합니다. 컨트롤은 UI UI 스레드가 아닌 스레드를 만든 스레드에서 건드려야합니다. 응용 프로그램에는 수많은 UI 스레드가있을 수 있으며 각 스레드에는 고유 한 컨트롤 집합이 있습니다. 따라서 다른 스레드에서 컨트롤을 만들면 Invoke 또는 BeginInvoke를 사용하여 모든 호출을 정렬하지 않고도 주 스레드에서 해당 컨트롤로 작업 할 수 있습니다.

편집 여러 UI 스레드에 대한 몇 가지 참조 :

MSDN on Message Loops MSDN social discussion Multiple threads in WPF

+0

이 소유권 주장에 대한 참조를 제공 할 수 있습니까? 내 이해는 메시지 루프를 처리하는 동일한 스레드가 컨트롤을 업데이트해야한다는 것입니다. –

+0

맞아. 내가 만들고자하는 점은 화면상의 요소를 업데이트하려고하지 않는다는 것이다. 이것은 모두 초기화 코드입니다. 양식이 표시되지 않았습니다. .NET 객체 구조를 초기 단계에서 벗어나려고 만 노력하고 있습니다. – Clyde

+0

Eric - N 개의 메시지 루프를 하나의 스레드 당 하나씩 가질 수 있습니다. 컨트롤에 스레드 유사성이 있습니다. Clyde - .Net 개체를 만드는 동작은 스레드가 생성 된 스레드에 바인딩 된 기본 Windows 개체를 만듭니다 . – dsolimano

14

그것을 하나 개의 스레드에서 양식을 만들 수는 없지만, 및 사용하여 표시해야 다른 스레드 인 경우 이며 주 GUI가 아닌 스레드에서 양식을 작성할 수 있습니다. 현재 받아 들여지는 대답은 이것이 가능하지 않다고 말하는 것 같습니다.

Windows Forms는 단일 스레드 아파트 모델을 시행합니다. 요약하면 이는 스레드 당 하나의 Window 메시지 루프 만 가능하며 그 반대도 마찬가지입니다. 또한 예를 들어 threadA가 threadB의 메시지 루프와 상호 작용하려는 경우 BeginInvoke와 같은 메커니즘을 통해 호출을 마샬링해야합니다.

그러나 새 스레드를 만들고 자체 메시지 루프를 제공하면 해당 스레드는 메시지 루프를 끝내야한다는 메시지가 전달 될 때까지 독립적으로 이벤트를 행복하게 처리합니다.

그래서 입증하기 위해, 아래의 Windows 만들고 비 GUI 스레드에서 양식을 표시하는 코드를 형성한다 :

public partial class Form1 : Form 
{ 
    public Form1() 
    { 
     InitializeComponent(); 
    } 

    private void Form1_Load(object sender, EventArgs e) 
    { 
     label1.Text = Thread.CurrentThread.ManagedThreadId.ToString(); 

    } 

    private void button1_Click(object sender, EventArgs e) 
    { 
     ThreadStart ts = new ThreadStart(OpenForm); 

     Thread t = new Thread(ts); 
     t.IsBackground=false; 

     t.Start(); 
    } 

    private void OpenForm() 
    { 
     Form2 f2 = new Form2(); 

     f2.ShowDialog(); 
    } 
} 


public partial class Form2 : Form 
{ 
    public Form2() 
    { 
     InitializeComponent(); 
    } 

    private void Form2_Load(object sender, EventArgs e) 
    { 
     label1.Text = Thread.CurrentThread.ManagedThreadId.ToString() ; 

    } 
} 

하는 OpenForm 방법은 새 스레드에서 실행을 Form2의 인스턴스를 만듭니다.

실제로 Form2는 ShowDialog()를 호출하여 자체 메시지 루프를 제공받습니다. 대신 Show()를 호출하면 메시지 루프가 제공되지 않고 Form2가 즉시 닫힙니다.

또한 OpenForm()에서 Form1에 액세스하려고하면 (예 : 'this'사용) 크로스 스레드 UI 액세스를 시도 할 때 런타임 오류가 발생합니다.

t.IsBackground=false은 스레드를 전경 스레드로 설정합니다. FormClosing 또는 FormClosed 이벤트를 먼저 호출하지 않고 기본 폼이 닫히면 백그라운드 스레드가 즉시 사라지기 때문에 전경 스레드가 필요합니다.

이제는 다른 형식과 마찬가지로 Form2를 사용할 수 있습니다. Form1은 여전히 ​​자신의 메시지 lopp로 평상시처럼 행복하게 운영되고 있음을 알 수 있습니다. 즉, 단추를 클릭하여 Form2의 여러 인스턴스를 만들 수 있습니다. 각 인스턴스는 자체 메시지 루프와 스레드로 구분됩니다.

이제 실제로 크로스 스레드 인 크로스 폼 액세스에주의해야합니다. 또한 주 양식이 아닌 모든 양식이 올바르게 닫히도록 주 양식의 닫기를 처리해야합니다.

+0

메시지 루프에 대한 이해는 무서운 것이지만 두 번째 메시지 루프 (실제로는 그렇게하는 것)를 실제로 회전시키는 것은 많은 일이 혼란 스러울 수 있기 때문에 일반적으로 좋은 생각이 아닙니다. 포커스, 탭, 키보드 및 마우스 캡처 등 –

+0

별도의 메시지 루프가있는 비디오, 애니메이션 등의 별도의 렌더링 창을 만들려는 경우 유용 할 수 있습니다. – Ash

1

비 UI 스레드에서 생성 된 구성 요소를 기본 UI에 추가 할 수 있다고 생각합니다.

따라서 'NewCompThread'및 'MainThread'스레드가 2 개 있습니다.

NewCompThread를 스핀 오프하면 MainUI (MainThread에서 작성)에 표시 할 준비가 된 구성 요소가 생성됩니다.

하지만 ... 당신이 NewCompThread에 이런 식으로 뭔가를 시도 할 경우 예외를 얻을 것이다 : ComponentCreatedOnNewCompTHread.parent = ComponentCreatedOnMainThread;

을하지만, 당신이 추가 할 수 있습니다

if (ComponentCreatedOnMainThread.InvokeRequired) { 
    ComponentCreatedOnMainThread.Invoke(appropriate delegate...); 
} else { 
    ComponentCreatedOnNewCompTHread.parent = ComponentCreatedOnMainThread; 
} 

그리고 그것은 작동합니다. 난 끝냈어.
이상한 점은 ComponentCreatedOnNewCompTHread가 MainThread에서 작성되었다고 생각하는 것입니다. ComponentCreatedOnNewCompTHread.InvokeRequired 그것이 TRUE를 반환합니다, 당신은 대리인을 만들고 다시 MainThread을 얻기 위해 호출을 사용해야합니다 :

당신하여 NewCompThread에서 다음을 수행합니다.

0

백그라운드 스레드에서 컨트롤을 만들 수 있지만 STA 스레드에서만 가능합니다.

내가 비동기 이것을 사용하기 위해 확장 메소드를 생성/패턴을

private async void treeview1_AfterSelect(object sender, TreeViewEventArgs e) 
{ 
    var control = await CreateControlAsync(e.Node); 
    if (e.Node.Equals(treeview1.SelectedNode) 
    { 
     panel1.Controls.Clear(); 
     panel1.Controls.Add(control); 
    } 
    else 
    { 
     control.Dispose(); 
    } 
} 

private async Control CreateControlAsync(TreeNode node) 
{ 
    return await Task.Factory.StartNew(() => CreateControl(node), ApartmentState.STA); 
} 

private Control CreateControl(TreeNode node) 
{ 
    // return some control which takes some time to create 
} 

기다리고이 확장 방법. 작업은 내부적으로 스레드를 사용하도록 아파트를 설정할 수 없습니다.

public static Task<T> StartNew<T>(this TaskFactory t, Func<T> func, ApartmentState state) 
{ 
    var tcs = new TaskCompletionSource<T>(); 
    var thread = new Thread(() => 
    { 
     try 
     { 
      tcs.SetResult(func()); 
     } 
     catch (Exception e) 
     { 
      tcs.SetException(e); 
     } 
    }); 
    thread.IsBackground = true; 
    thread.SetApartmentState(state); 
    thread.Start(); 
    return tcs.Task; 
} 
관련 문제