새 스레드에서 간단한 코드를 어떻게 실행합니까?


340

GUI가 아닌 다른 스레드에서 실행 해야하는 약간의 코드가 있습니다. 현재 코드가 실행되는 동안 (10 초 정도) 양식이 정지됩니다.

이전에 새 스레드를 만든 적이 없다고 가정합니다. C #에서 .NET Framework 2.0 이상을 사용하는 방법에 대한 간단한 / 기본 예는 무엇입니까?


2
여기에 나온 대부분의 답변은 당시에는 좋았지 만 .NET Framework 4.0의 개선으로 인해 일이 더 간단 해졌습니다. 이 답변에 설명 된대로 Task.Run () 메서드를 사용할 수 있습니다. stackoverflow.com/a/31778592/1633949
Richard II

답변:


336

읽기 시작하기 좋은 곳은 Joe Albahari 입니다.

자신 만의 스레드를 만들고 싶다면 다음과 같이 간단합니다.

using System.Threading;
new Thread(() => 
{
    Thread.CurrentThread.IsBackground = true; 
    /* run your code here */ 
    Console.WriteLine("Hello, world"); 
}).Start();

@EdPower, 이것이 Winforms에만 적용됩니까? 아니면 Web Forms ..에서 작동합니까?
MethodMan

@MethodMan-예, Web Forms에서 작동합니다. 여기에서 시작 :
에드 전원

9
IsBackgroundtrue로 설정하는 데 주의하십시오 . 아마도 당신이 생각하는 것을하지 않을 것입니다. 그 기능은 모든 포 그라운드 스레드가 사망했을 때 스레드가 종료되는지 또는 스레드가 애플리케이션을 계속 유지할지 여부를 구성하는 것입니다. 실행 도중 스레드가 종료되지 않게 IsBackground하려면 true로 설정하지 마십시오 .
Zero3

10
@ EdPower 나는 당신이 어느 쪽이든 조심해야한다고 생각합니다! 실행 도중에 종료하고 싶지 않은 작업의 예는 데이터를 디스크에 저장하는 작업입니다. 그러나 언제든지 작업이 종료에 적합한 경우 플래그가 좋습니다. 내 요점은 플래그를 사용하는 것에 대해주의해야한다는 것입니다 . 왜냐하면 그 목적을 설명하지 않았기 때문에 이름을 지정하면 실제로 수행하는 것보다 다른 일을한다고 믿을 수 있습니다.
Zero3

3
.NET Framework 4.0 이상에서는 다음 답변에 설명 된대로 Task.Run ()을 사용하십시오 : stackoverflow.com/a/31778592/1633949
Richard II

193

BackgroundWorker 최선의 선택 인 것 같습니다.

여기에 최소한의 예가 있습니다. 단추를 클릭하면 백그라운드 작업자가 백그라운드 스레드에서 작업을 시작하고 진행 상황을 동시에보고합니다. 작업이 완료된 후에도보고합니다.

using System.ComponentModel;
...
    private void button1_Click(object sender, EventArgs e)
    {
        BackgroundWorker bw = new BackgroundWorker();

        // this allows our worker to report progress during work
        bw.WorkerReportsProgress = true;

        // what to do in the background thread
        bw.DoWork += new DoWorkEventHandler(
        delegate(object o, DoWorkEventArgs args)
        {
            BackgroundWorker b = o as BackgroundWorker;

            // do some simple processing for 10 seconds
            for (int i = 1; i <= 10; i++)
            {
                // report the progress in percent
                b.ReportProgress(i * 10);
                Thread.Sleep(1000);
            }

        });

        // what to do when progress changed (update the progress bar for example)
        bw.ProgressChanged += new ProgressChangedEventHandler(
        delegate(object o, ProgressChangedEventArgs args)
        {
            label1.Text = string.Format("{0}% Completed", args.ProgressPercentage);
        });

        // what to do when worker completes its task (notify the user)
        bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(
        delegate(object o, RunWorkerCompletedEventArgs args)
        {
            label1.Text = "Finished!";
        });

        bw.RunWorkerAsync();
    }

노트 :

  • 단순화를 위해 C #의 익명 메소드를 사용하여 모든 것을 단일 메소드에 넣었지만 항상 다른 메소드로 가져올 수 있습니다.
  • 이 업데이트에서 GUI에 안전 ProgressChanged또는 RunWorkerCompleted핸들러. 그러나 GUI를 업데이트 DoWork 하면에서 발생 InvalidOperationException합니다.

27
System.ComponentModel 사용; (이 유용한 코드 예제 덕분에 사람들이 Google 검색을하지 못하게 할 수도 있습니다.) +1
sooprise

@Gant 샘플 코드에 감사드립니다. 코드를 시도했을 때 ProgressChanged 이벤트가 시작되지 않았습니다. 내가 여기에 유사한 허용 대답을 시도하지만 때 stackoverflow.com/questions/23112676/... 는했다.
Martin

1
@sooprise Ctrl +. 이러한 상황에서 당신을 도와줍니다! (Visual Studio를 사용한다고 가정)
SepehrM

100

ThreadPool.QueueUserWorkItem는 뭔가 간단한 꽤 이상적입니다. 유일한주의 사항은 다른 스레드에서 컨트롤에 액세스하는 것입니다.

System.Threading.ThreadPool.QueueUserWorkItem(delegate {
    DoSomethingThatDoesntInvolveAControl();
}, null);

또한 내가 좋아하는 것. 정션을 위한 하나의 빠른 라인 . 단축 다이얼 (스 니펫)에 있습니다. 컨트롤과 매우 잘 작동합니다. InvokeInvokeRequired 사용 방법을 알아야합니다 .
Bitterblue

1
+1 델리게이트를 사용하면 매개 변수를 깔끔하게 래핑 할 수 있습니다.
Will Bickford

콜백 함수와 함께 ThreadPool.QueueUserWorkItem을 사용하는 방법은 작업이 완료되면 콜백이 알려줍니다.
Mou

76

빠르고 더럽지 만 작동합니다.

상단에 사용 :

using System.Threading;

간단한 코드 :

static void Main( string[] args )
{
    Thread t = new Thread( NewThread );
    t.Start();
}

static void NewThread()
{
    //code goes here
}

방금 이것을 exmaple을위한 새로운 콘솔 응용 프로그램으로 던졌습니다.


8
IsBackground로 표시된 스레드는 런타임에 의해 자동으로 종료되지 않습니다. 이를 위해서는 앱의 스레드 관리가 필요합니다. 스레드가 백그라운드가 아닌 것으로 표시되면 스레드 실행 후 스레드가 종료됩니다.
CmdrTallen 2016 년

14
@CmdrTallen : 맞지 않습니다. 스레드는 스레드가 IsBackground = 거짓 모든 스레드가 종료 한 때 종료됩니다 즉에게 프로세스를 종료에서 프로세스를 중지하지 않습니다 IsBackground = 진정한 의미를 표시
필 드 바니

66

다른 옵션은 다음과 같습니다.

Task.Run(()=>{
//Here is a new thread
});

1
이 방법으로 스레드를 만들면 UI 스레드 에서이 스레드를 어떻게 중지합니까?
slayernoah

1
@slayernoah CancellationTokenSource를 매개 변수로 전달하고 스레드 외부에 취소 토큰을 설정할 수 있습니다. msdn.microsoft.com/en-us/library/dd997364(v=vs.110).aspx
Spongebob Comrade

7
이 코드는 새로운 스레드를 보장하지 않습니다. 결정은 사용중인 ThreadPool의 현재 구현에 달려 있습니다. 예제로보기 stackoverflow.com/questions/13570579/...
줄리오 Caccin에게

3
@GiulioCaccin ThreadPool이 풀에서 기존 스레드를 선택할 수는 있지만 확실히 다른 스레드입니다.
스폰지 밥 동지

40

BackgroundWorker 클래스를 사용해보십시오 . 실행할 대상을 위임하고 작업이 완료되면 알림을받습니다. 내가 링크 한 MSDN 페이지에 예가 있습니다.


6
Thread 클래스를 사용하여이 작업을 수행 할 수는 있지만 BackgroundWorker는 스레드 사용 및 진행률보고를위한 방법을 제공합니다. UI와 대화하려면 Invoke를 사용해야한다는 것을 잊지 마십시오!
Robert Rossney

1
경고 : BackgroundWorker에는 몇 가지 미묘한 제한이 있지만 일반적인 "내가 가고 싶은 일을하고 응답 성을 유지하고 싶다"는 것이 환상적입니다.
Merus

10
@Merus, 그 미묘한 한계가 무엇인지 확장 할 수 있습니까?
Christian Hudon

14

가치를 얻고 싶다면 :

var someValue;

Thread thread = new Thread(delegate()
            {                 
                //Do somthing and set your value
                someValue = "Hello World";
            });

thread.Start();

while (thread.IsAlive)
  Application.DoEvents();

6

해당 코드를 함수 (GUI와 동일한 스레드에서 실행할 수없는 코드)에 넣고 해당 코드의 실행을 트리거하려면 다음을 입력하십시오.

Thread myThread= new Thread(nameOfFunction);

workerThread.Start();

스레드 객체에서 시작 함수를 호출하면 새 스레드에서 함수 호출이 실행됩니다.


3
@ user50612 무슨 소리 야? 새 스레드는 nameOfFunction현재 GUI 스레드가 아닌 백그라운드에서 실행 됩니다. 이 IsBackground속성은 스레드가 응용 프로그램을 유지할지 여부를 결정합니다. msdn.microsoft.com/en-us/library/…
Zero3

5

다음은 progressBar와 함께 스레드를 사용하는 방법으로, 스레드가 작동하는 방식을 설명하기 위해 3 개의 progressBar 및 4 버튼이 있습니다.

 public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }
    Thread t, t2, t3;
    private void Form1_Load(object sender, EventArgs e)
    {

        CheckForIllegalCrossThreadCalls = false;

         t = new Thread(birinicBar); //evry thread workes with a new progressBar


         t2 = new Thread(ikinciBar);


         t3 = new Thread(ucuncuBar);

    }

    public void birinicBar() //to make progressBar work
    {
        for (int i = 0; i < 100; i++) {
            progressBar1.Value++;
            Thread.Sleep(100); // this progressBar gonna work faster
        }
    }

    public void ikinciBar()
    {
        for (int i = 0; i < 100; i++)
        {
            progressBar2.Value++;
            Thread.Sleep(200);
        }


    }

    public void ucuncuBar()
    {
        for (int i = 0; i < 100; i++)
        {
            progressBar3.Value++;
            Thread.Sleep(300);
        }
    }

    private void button1_Click(object sender, EventArgs e) //that button to start the threads
    {
        t.Start();
        t2.Start(); t3.Start();

    }

    private void button4_Click(object sender, EventArgs e)//that button to stup the threads with the progressBar
    {
        t.Suspend();
        t2.Suspend();
        t3.Suspend();
    }

    private void button2_Click(object sender, EventArgs e)// that is for contuniue after stuping
    {
        t.Resume();
        t2.Resume();
        t3.Resume();
    }

    private void button3_Click(object sender, EventArgs e) // finally with that button you can remove all of the threads
    {
        t.Abort();
        t2.Abort();
        t3.Abort();
    }
}

3
// following declaration of delegate ,,,
public delegate long GetEnergyUsageDelegate(DateTime lastRunTime, 
                                            DateTime procDateTime);

// following inside of some client method
GetEnergyUsageDelegate nrgDel = GetEnergyUsage;
IAsyncResult aR = nrgDel.BeginInvoke(lastRunTime, procDT, null, null);
while (!aR.IsCompleted) Thread.Sleep(500);
int usageCnt = nrgDel.EndInvoke(aR);

Charles (위) 코드가 올바르지 않습니다. 완료를 기다릴 필요가 없습니다. EndInvoke는 WaitHandle이 신호를 보낼 때까지 차단됩니다.

완료 될 때까지 차단하려면 간단히

nrgDel.EndInvoke(nrgDel.BeginInvoke(lastRuntime,procDT,null,null));

또는 대안 적으로

ar.AsyncWaitHandle.WaitOne();

그러나 차단하면 anyc 호출을 발행하는 요점은 무엇입니까? 동기 호출을 사용할 수도 있습니다. 더 나은 방법은 정리를 위해 람다를 차단하고 전달하지 않는 것입니다.

nrgDel.BeginInvoke(lastRuntime,procDT,(ar)=> {ar.EndInvoke(ar);},null);

명심 해야 할 것은 EndInvoke 호출 해야 한다는 것 입니다. 대부분의 비동기 구현이 EndInvoke에서 waithandle을 해제함에 따라 많은 사람들이 이것을 잊고 WaitHandle을 유출시킵니다.


2

원시 Thread 객체를 사용하려는 경우 IsBackground를 최소한 true로 설정해야하며 Threading Apartment 모델 (아마도 STA)도 설정해야합니다.

public static void DoWork()
{
    // do some work
}

public static void StartWorker()
{
    Thread worker = new Thread(DoWork);
    worker.IsBackground = true;
    worker.SetApartmentState(System.Threading.ApartmentState.STA);
    worker.Start()
}

UI 상호 작용이 필요한 경우 BackgroundWorker 클래스를 권장합니다.


IsBackground를 true로 설정하는 데주의하십시오. 아마도 당신이 생각하는 것을하지 않을 것입니다. 그 기능은 모든 포 그라운드 스레드가 사망했을 때 스레드가 종료되는지 또는 스레드가 애플리케이션을 계속 유지할지 여부를 구성하는 것입니다. 실행 도중 스레드가 종료되지 않게하려면 IsBackground를 true로 설정하지 마십시오.
Zero3


1

델리게이트와 스레드 풀을 사용하는 또 다른 옵션은 ...

'GetEnergyUsage'는 DateTime과 다른 DateTime을 입력 인수로 사용하고 Int를 반환하는 메서드라고 가정합니다.

// following declaration of delegate ,,,
public delegate long GetEnergyUsageDelegate(DateTime lastRunTime, 
                                            DateTime procDateTime);

// following inside of some client method 
GetEnergyUsageDelegate nrgDel = GetEnergyUsage;                     
IAsyncResult aR = nrgDel.BeginInvoke(lastRunTime, procDT, null, null);
while (!aR.IsCompleted) Thread.Sleep(500);
int usageCnt = nrgDel.EndInvoke(aR);

1
찰스, 코드가 기능적으로 다른 int usageCnt = nrgDel.Invoke(lastRunTime, procDT, null, null);가요? 어쨌든 수면 상태에서 현재 스레드를 일시 중단하는 것처럼 보입니다 ... GUI 스레드에서 호출하면 GUI가 멈추는 데 도움이되지 않는다고 생각했습니다.
IgorK

예, BeginInvoke는 스레드 풀에서 다른 스레드의 델리게이트를 호출합니다 ... Invoke를 사용하는 경우 현재 스레드에서 델리게이트가 동기식으로 호출됩니다. 콜백 함수를 사용하여 결과를 수집하십시오. 나는 그것을 보여주고 편집 할 수 있습니다
찰스 Bretana에게

1

.Net에서 별도의 스레드를 실행하는 방법은 여러 가지가 있으며 각 동작은 서로 다릅니다. GUI가 종료 된 후 스레드를 계속 실행해야합니까? 스레드와 GUI간에 정보를 전달해야합니까? 스레드가 GUI를 업데이트해야합니까? 스레드가 한 작업을 수행 한 다음 종료해야합니까, 아니면 계속 실행해야합니까? 이 질문에 대한 답변은 어떤 방법을 사용해야하는지 알려줍니다.

Code Project 웹 사이트에는 다양한 방법을 설명하고 샘플 코드를 제공 하는 좋은 비동기 방법 기사 가 있습니다.

이 기사는 async / await 패턴과 Task Parallel Library 가 .NET에 도입 되기 전에 작성 되었습니다.


이것은 내가 찾고있는 정보의 유형이지만 귀하의 답변은 링크 썩음의 희생자입니다.
Chris

알려 주셔서 감사합니다, @Chris; 링크 고정.
Dour High Arch

0

방법 : 백그라운드 스레드를 사용하여 파일 검색

다른 스레드에서 GUI 관련 항목에 액세스하는 데 매우주의해야합니다 (많은 GUI 툴킷에서 일반적 임). 처리 스레드에서 GUI에서 무언가를 업데이트하려면 이 답변 을 확인하십시오 .WinForms에 유용하다고 생각합니다. WPF의 경우이 내용을 참조 하십시오 (UpdateProgress () 메서드의 구성 요소를 터치하여 다른 스레드에서 작동하는 방법을 보여 주지만 실제로 Dispathcer를 통해 수행 CheckAccess()하기 전에 수행 하지 않는 것이 마음에 들지 않습니다 BeginInvoke.CheckAccess 참조 및 검색)

스레딩에 .NET의 특정 책을보고 발견 이 하나 (무료 다운로드)를. 자세한 내용은 http://www.albahari.com/threading/ 을 참조 하십시오 .

처음 20 페이지에서 새로운 스레드로 실행을 시작하는 데 필요한 것을 찾을 수 있다고 생각합니다. 더 많은 스레드가 있습니다 (스레딩에만 엄격하게 적용되는 GUI 특정 스 니펫에 대해서는 확실하지 않습니다). 내가이 글을 읽고 있기 때문에이 작업에 대해 커뮤니티가 어떻게 생각하는지 들으면 기쁠 것입니다. 지금은 나에게 깔끔하게 보였다 (스레딩에 대한 .NET 특정 메소드 및 유형 표시). 또한 .NET 2.0 (그리고 고대 1.1은 아님)에 대해 정말로 감사합니다.


0

Jeff Richter의 Power Threading Library 와 특히 IAsyncEnumerator를 살펴 보는 것이 좋습니다 . 의 비디오를 살펴보세요 찰리 칼 버트의 리히터 좋은 개요를 위해 위에 어디로 블로그.

비동기 프로그래밍 작업을 쉽게 코딩 할 수 있기 때문에 이름을 사용하지 마십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.