Как запустить простой код в новом потоке?

У меня есть немного кода, который мне нужно запустить в другом потоке, чем GUI, так как в настоящее время он вызывает зависание формы во время выполнения кода (10 секунд или около того).

Предположим, я никогда раньше не создавал новую тему; Что является простым / базовым примером того, как сделать это в C # и с помощью .NET Framework 2.0 или более поздней версии?

12.12.2008 16:53:49
Большинство ответов здесь были хорошими в то время, но улучшения в .NET Framework 4.0 упрощают ситуацию. Вы можете использовать метод Task.Run (), как описано в этом ответе: stackoverflow.com/a/31778592/1633949
Richard II 25.10.2019 13:49:17
15 ОТВЕТОВ

Попробуйте использовать класс BackgroundWorker . Вы даете ему делегаты на то, что запускать, и получать уведомления, когда работа закончена. На странице MSDN есть пример, на который я ссылаюсь.

40
12.12.2008 16:56:54
Конечно, вы можете сделать это с помощью класса Thread, но BackgroundWorker предоставляет вам методы для завершения потока и отчета о ходе работы, которые в противном случае вам пришлось бы выяснять, как использовать себя. Не забывайте, что вы должны использовать Invoke для общения с пользовательским интерфейсом!
Robert Rossney 12.12.2008 18:49:09
Будьте осторожны: у BackgroundWorker есть некоторые тонкие ограничения, но для обычного «я хочу уйти и сделать что-то, чтобы моя форма оставалась отзывчивой», это фантастика.
Merus 28.01.2009 00:49:46
@Merus, не могли бы вы рассказать, что это за тонкие ограничения?
Christian Hudon 18.09.2012 20:34:21

Быстро и грязно, но сработает

Использование сверху:

using System.Threading;

простой код:

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

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

Я просто бросил это в новое консольное приложение для примера

75
12.12.2008 16:59:39
Помните, что потоки, помеченные IsBackground, автоматически не завершаются средой выполнения. Это требует управления потоками приложением. Если вы оставите поток помеченным как не фоновый, то после выполнения потока поток прерывается для вас.
CmdrTallen 16.03.2009 14:29:13
@CmdrTallen: Это не совсем верно. Поток, помеченный IsBackground = true, означает, что поток не остановит выход из процесса, т.е. процесс завершится, когда все потоки с IsBackground = false вышли
Phil Devaney 6.08.2009 15:42:05

ThreadPool.QueueUserWorkItem довольно идеально подходит для чего - то просто. Единственное предостережение - доступ к элементу управления из другого потока.

System.Threading.ThreadPool.QueueUserWorkItem(delegate {
    DoSomethingThatDoesntInvolveAControl();
}, null);
100
12.12.2008 16:59:50
Тоже мой любимый. Одна быстрая линия для соединения . У меня есть это на быстром наборе (фрагмент). Очень хорошо работает с элементами управления. Вам просто нужно знать, как использовать Invoke и InvokeRequired .
Bitterblue 8.07.2014 11:34:17
+1 Обратите внимание, что делегат также позволяет аккуратно переносить параметры.
Will Bickford 21.08.2014 19:17:51
Как использовать ThreadPool.QueueUserWorkItem с функцией обратного вызова означает, что когда работа выполнена, обратный вызов уведомит нас.
Mou 22.02.2017 12:27:39

Поместите этот код в функцию (код, который не может быть выполнен в том же потоке, что и GUI), и для запуска выполнения этого кода поместите следующее.

Thread myThread= new Thread(nameOfFunction);

workerThread.Start();

Вызов функции start для объекта потока приведет к выполнению вызова вашей функции в новом потоке.

6
12.12.2008 17:00:47
-1. Как минимум, вам нужно установить IsBackground на true.
user50612 26.01.2009 20:16:49
@ user50612 О чем ты говоришь? Новый поток будет работать nameOfFunctionв фоновом режиме, а не в текущем потоке графического интерфейса. IsBackgroundСвойство определяет , будет ли поток держать приложение в живых или нет: msdn.microsoft.com/en-us/library/...
Zero3 22.09.2016 13:29:35

другой вариант, который использует делегаты и пул потоков ...

Предполагая, что 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
12.12.2008 17:14:07
Чарльз, твой код функционально отличается от int usageCnt = nrgDel.Invoke(lastRunTime, procDT, null, null);? Похоже, что он все равно приостанавливает текущий поток со сном ... Я думал, что это ничего не поможет с зависанием GUI, если вы
IgorK 12.12.2008 17:45:22
Да, BeginInvoke вызывает делегата в другом потоке из пула потоков ... Если вы используете Invoke, делегат вызывается синхронно в текущем потоке ... Но вы правы, сон не так ... вы должны устранить это и собрать результаты с помощью функции обратного вызова. Я отредактирую, чтобы показать это
Charles Bretana 12.12.2008 17:54:14

Как: использовать фоновую тему для поиска файлов

Вы должны быть очень осторожны с доступом из других потоков к конкретным графическим интерфейсам (это характерно для многих GUI-инструментариев). Если вы хотите обновить что-либо в GUI из потока обработки, проверьте ответ, который, на мой взгляд, полезен для WinForms. Для WPF смотрите это (он показывает, как прикоснуться к компоненту в методе UpdateProgress (), чтобы он работал из других потоков, но на самом деле мне не нравится, что он не делает, CheckAccess()прежде чем делать BeginInvokeчерез Dispathcer, смотрите и ищите CheckAccess в нем)

Искал .NET конкретную книгу о потоке и нашел этот (бесплатно загружаемый). Смотрите http://www.albahari.com/threading/ для более подробной информации об этом.

Я полагаю, что вы найдете то, что вам нужно для запуска исполнения в виде нового потока на первых 20 страницах, и у него есть еще много (не уверен насчет специфических фрагментов GUI, я имею в виду строго специфический для потоков). Был бы рад услышать, что сообщество думает об этой работе, потому что я читаю эту. На данный момент выглядело довольно аккуратно для меня (для показа .NET конкретных методов и типов для многопоточности). Также он охватывает .NET 2.0 (а не древний 1.1), что я действительно ценю.

0
23.05.2017 11:55:01

В .Net существует множество способов запуска отдельных потоков, каждый из которых имеет свое поведение. Вам нужно продолжить запуск потока после выхода из GUI? Нужно ли передавать информацию между потоком и графическим интерфейсом? Поток должен обновить GUI? Должен ли поток выполнить одну задачу, а затем завершиться или продолжить работу? Ответы на эти вопросы скажут вам, какой метод использовать.

На веб-сайте Code Project есть хорошая статья об асинхронных методах, в которой описаны различные методы и приведен пример кода.

Обратите внимание, что эта статья была написана до того, как в .NET были введены шаблон async / await и Task Parallel Library .

1
26.03.2018 16:54:21
Это информация, которую я искал, но ваш ответ является жертвой гниения ссылок.
Chris 26.03.2018 16:46:24
Спасибо, что сообщили мне, @Chris; ссылка исправлена.
Dour High Arch 26.03.2018 16:54:41
// 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);

Чарльз, твой код (выше) не верен. Вам не нужно вращаться, дождитесь завершения. EndInvoke будет блокироваться до тех пор, пока не будет получен сигнал WaitHandle.

Если вы хотите заблокировать до завершения, вам просто нужно

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

или в качестве альтернативы

ar.AsyncWaitHandle.WaitOne();

Но какой смысл делать вызовы anyc, если вы блокируете? Вы могли бы также просто использовать синхронный вызов. Лучше было бы не блокировать и передавать лямбду для очистки:

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

Следует иметь в виду, что вы должны вызывать EndInvoke. Многие люди забывают об этом и в конечном итоге пропускают WaitHandle, поскольку большинство асинхронных реализаций выпускают дескриптор ожидания в EndInvoke.

3
1.08.2018 06:45:11

Если вы собираетесь использовать необработанный объект 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()
}

Я бы порекомендовал класс BackgroundWorker, если вам нужно взаимодействие с пользовательским интерфейсом.

2
1.08.2018 06:13:25
Будьте внимательны при установке IsBackground в значение true. Это, вероятно, не делает то, что вы думаете, что делает. Что он делает, это настраивает, будет ли поток уничтожен, когда все потоки переднего плана умерли, или будет ли поток поддерживать приложение живым. Если вы не хотите, чтобы ваш поток завершался в середине выполнения, не устанавливайте IsBackground в true.
Zero3 22.09.2016 13:35:52
Zero3 22.09.2016 13:36:20

Я бы порекомендовал взглянуть на Power Threading Library Джеффа Рихтера и, в частности, на IAsyncEnumerator. Взгляните на видео в блоге Чарли Калверта, где Рихтер просматривает его для хорошего обзора.

Не откладывайте на это имя, потому что это облегчает программирование задач асинхронного программирования.

0
27.01.2009 02:22:47

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.
193
21.08.2014 16:49:54
Использование System.ComponentModel; (может спасти людей от поиска в Google, спасибо за этот полезный пример кода) +1
sooprise 7.03.2011 19:04:17
@Gant Спасибо за пример кода. Когда я попробовал ваш код, событие ProgressChanged не было запущено. Однако когда я попробовал подобный принятый ответ здесь [ stackoverflow.com/questions/23112676/… это сработало.
Martin 4.09.2014 12:47:02
@sooprise Ctrl +. помогает вам в этих ситуациях! (Предполагая, что вы используете Visual Studio)
SepehrM 13.11.2014 18:20:58

Если вы хотите получить значение:

var someValue;

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

thread.Start();

while (thread.IsAlive)
  Application.DoEvents();
14
7.08.2009 03:28:56

Хорошее место для начала чтения - Джо Албахари .

Если вы хотите создать свой собственный поток, это так просто:

using System.Threading;
new Thread(() => 
{
    Thread.CurrentThread.IsBackground = true; 
    /* run your code here */ 
    Console.WriteLine("Hello, world"); 
}).Start();
333
11.08.2017 13:40:52
@EdPower, это относится только к Winforms .. или это будет работать в веб-формах ..?
MethodMan 28.01.2016 03:31:45
@MethodMan - Да, это будет работать в веб-формах. Начните здесь:
Ed Power 28.01.2016 19:11:44
Будьте осторожны, устанавливая IsBackgroundзначение true. Это, вероятно, не делает то, что вы думаете, что делает. Что он делает, это настраивает, будет ли поток уничтожен, когда все потоки переднего плана умерли, или будет ли поток поддерживать приложение живым. Если вы не хотите, чтобы ваш поток завершался в середине выполнения, не устанавливайте IsBackgroundзначение true.
Zero3 22.09.2016 13:34:13
@EdPower Я думаю, вы должны быть осторожны в любом случае! Одним из примеров задачи, которую вы, вероятно, не хотите завершать в середине выполнения, является та, которая сохраняет данные на диск. Но конечно, если ваша задача подходит для завершения в любое время, флаг в порядке. Моя точка зрения заключалась в том, что нужно быть осторожным с использованием флага , поскольку вы не описали его назначение, и его наименование может легко привести к мысли, что он делает что-то еще, а не то, что на самом деле делает.
Zero3 23.09.2016 08:44:22
с .NET Framework 4.0+ просто используйте Task.Run (), как описано в этом ответе: stackoverflow.com/a/31778592/1633949
Richard II 25.10.2019 13:58:13

Вот еще один вариант:

Task.Run(()=>{
//Here is a new thread
});
65
3.08.2015 02:25:17
Точный пример кода, который вы ожидаете от
Mohsen Shakiba 1.04.2016 19:45:31
если вы создадите поток таким образом, как вы остановите этот поток из потока пользовательского интерфейса?
slayernoah 6.05.2016 17:44:19
@slayernoah, вы можете передать CancellationTokenSource в качестве параметра и установить токен отмены вне потока: msdn.microsoft.com/en-us/library/dd997364(v=vs.110).aspx
Spongebob Comrade 9.05.2016 02:35:16
@ MohsenShakiba, меня интересует, почему это не лучший подход? Это, конечно, очень кратко (и метод, который я использовал некоторое время).
Anth12 5.12.2016 14:53:40
Этот код не гарантирует, что у вас будет новая тема. Решение зависит от текущей реализации ThreadPool, который вы используете. См. В качестве примера stackoverflow.com/questions/13570579/…
Giulio Caccin 5.09.2017 12:51:29

Вот как можно использовать потоки с progressBar, просто для того, чтобы понять, как работают потоки, в форме есть три 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();
    }
}
5
15.10.2016 14:34:42