[C#] Многопоточность

Discussion in 'С/С++, C#, Rust, Swift, Go, Java, Perl, Ruby' started by zoke, 4 Mar 2012.

Thread Status:
Not open for further replies.
  1. zoke

    zoke Member

    Joined:
    5 Sep 2010
    Messages:
    90
    Likes Received:
    9
    Reputations:
    5
    Вот не большой урок многопоточности в C#.
    Подойдёт только для простых программы.

    Для начала добавим элемент.
    У меня вот так выглядит.

    Почти весь код прокомментирован так что я здесь объяснять не буду.

    [​IMG]

    Далее добавить директивы using
    Code:
    using System;
    using System.Text;
    using System.Windows.Forms;
    using System.IO;
    using System.Net;
    using System.Threading;
    
    Объявим переменные
    Code:
    Thread[] threads; // Обьявляем массив потоков
            int i = 0; // позиция
            String[] lines = null; // Массив сайтов
            Object locks = new Object(); // Object для lock
    Добавим такой код на кнопку "старт".

    Code:
    lines = richTextBox1.Lines;
                i = 0;
                if (lines.Length < 1) 
                {
                    MessageBox.Show(this, "Слишком мало сайтов, попробуйте снова!", "Threads Example", MessageBoxButtons.OK, MessageBoxIcon.Stop);
                    return;
                    // завершаем если строк меньше 1
                }
                threads = new Thread[(int)numericUpDown1.Value]; 
                for (int q = 0; q < threads.Length; q++)
                {
                    threads[q] = new Thread(main); // поток будет запускать функцию main
                    threads[q].Start(); // запускаем
    }
    new Thread(waitth).Start();
    
    Добавим две функций. Для отправки get запроса и ожидание завершения работы потоков.

    Code:
    private void waitth()
            {
                for (int q = 0; q < threads.Length; q++)
                {
                    threads[i].Join(); // ожидаем когда потом закончится
                }
                MessageBox.Show(this, "Работа завершена!", "Threads Example", MessageBoxButtons.OK, MessageBoxIcon.Stop);   
            }
            public string get(string _url, CookieCollection Cooks) // функция для отправки get запроса
            {
                try
                {
                    HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(_url);
                    webRequest.CookieContainer = new CookieContainer();
                    webRequest.AllowAutoRedirect = false; // автоматическая переадресация
                    webRequest.Timeout = 30000; // время ожидания загрузки страницы
                    webRequest.UserAgent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.9.0.19) Gecko/2010031422 Firefox/3.0.19";
                    
                    if (Cooks != null)
                        webRequest.CookieContainer.Add(Cooks);
                    using (HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse())
                    {
                        webResponse.Cookies = webRequest.CookieContainer.GetCookies(webRequest.RequestUri);
                        if (webResponse.Cookies != null)
                            Cooks.Add(webResponse.Cookies);
                        string dataContent = new StreamReader(webResponse.GetResponseStream(), Encoding.GetEncoding(1251)).ReadToEnd();
                        string end = webResponse.Headers.ToString();
                        return end + dataContent;
                    }
                }
                catch
                {
                    return null; // если была ошибка в загрузке страницы возвращаем null
                }
            }
    На кнопку стоп
    Code:
    for (int q = 0; q < threads.Length; q++)
                {
                    threads[i].Abort(); // прерываем поток
                }
    Ну и главная функция

    Code:
    private void main()
            {
                while (true)
                {
                    string url;
                    lock (locks) // доступ только с одного потока, блокирует доступ с остальных и они в процессе ожидания.
                    {
                        if (i > lines.Length - 1) // проверяем не весь ли массив прошли
                        {
                            break; // останавливаем поток
                        }
                        url = lines[i]; // здесь понятно
                        i++; // всё равно что i = i +1; или i += 1;
                        if (!url.Contains("http://"))  // проверяем, если в линке нету http:// начинаем цикл заново. url.Contains("http://") == False - тоже самое
                        {
                            continue; // завершаем потом
                        }
                    }
                    CookieCollection Cooks = new CookieCollection(); // создаём куки менеджер, автоматическии обрабатытает куки
                    get(url, Cooks); // отправляем get запрос
    
                }
            }
    Если будут вопросы пишите, особы я не тестировал.
    Если что-то не так будет пишите.


    Скачать исходники.

     
    #1 zoke, 4 Mar 2012
    Last edited: 21 Mar 2012
    2 people like this.
  2. Bers

    Bers Member

    Joined:
    17 May 2010
    Messages:
    78
    Likes Received:
    30
    Reputations:
    26
    Каждый раз, когда я вижу очередную "статью" на тему многопоточности, у меня возникает такое неприятное ощущение в яичках, как будто они подкатывают к горлу, вызывая одновременно тошноту и опасение, что я не сдержусь и блевану ими прямо на монитор. Уверен, это ощущение знакомо многим.
    И на этот раз оно меня снова не подвело.



    Замечательное начало статьи - wait в UI-потоке. А потом удивляемся, почему это программы на .NET такие глючные - а вот и ответ - потому что их пишут люди, начитавшиеся всяких там zoke


    coding style? Не, не слышал.
    Дам совет - именуй все переменные, функции и классы так, чтобы было удобно тебе, не думай о тех людях, которые будут потом читать твой код. Если тебе удобно писать вот так:
    PHP:
    public CTSDF_string_w45 My_csr_s234_getWHGAS_CString_R123(CTSDF_string_w45 _url123_skahndpq3_lstCOOOKZ_SZF10_LIST PE4ENKI_BLJAD)
    не слушай никого, так и пиши!

    IDisposable? Нет, не слышал.

    Ты немного неправ, позволь показать тебе более корректный вариант:
    PHP:
    catch
    {
       return 
    null// если была ошибка в загрузке страницы,
                    // если наступил апокалипсис
                    // если зомби захватили мир
                    // если путин стал президентом
                    //   да пофиг, вернем null и все дела
    }

    Круто, я еще не видел, чтобы человек объяснял, что такое блокирующая синхронизация и как ее использовать, в одну строчку. Хотя нет, один раз ко мне на собеседование пришел парень лет 30, который на просьбу рассказать что-нибудь про многопоточный код ответил: "да я вообще не сталкивался с многопоточностью, нахрен она вообще нужна-то".

    Сжалься над моими мозгами! Зачем, ну зачем ты создаешь пустую коллекцию и передаешь ее в параметр функции, если эта коллекция один хрен не используется?!

    Зато я на днях такую "особу" оттестировал, ммм...


    Вообще для меня статья о многопоточности, в которой нет ни слова о многопоточности (как в этой например) - символ чего-то злого, не из нашего светлого мира. Пойду-ка лучше вызову батюшку, компьютер святой водой окропить да молебен прочесть...
     
    1 person likes this.
  3. zoke

    zoke Member

    Joined:
    5 Sep 2010
    Messages:
    90
    Likes Received:
    9
    Reputations:
    5
    Честно говоря я и думал что такая реакция будет. Ты не много прав.
    Можешь показать как ты многопоточность делаешь?
     
    #3 zoke, 5 Mar 2012
    Last edited: 5 Mar 2012
  4. sparsame

    sparsame Member

    Joined:
    28 Oct 2009
    Messages:
    0
    Likes Received:
    5
    Reputations:
    0
    Спасибо большое, познавательно получилось.
     
  5. seosimf

    seosimf Member

    Joined:
    3 Mar 2011
    Messages:
    271
    Likes Received:
    44
    Reputations:
    6
    ЧСВ так давит на зрения что не видно нихера?
     
  6. zoke

    zoke Member

    Joined:
    5 Sep 2010
    Messages:
    90
    Likes Received:
    9
    Reputations:
    5
    Это моя ошибка, забыл эту строку добавить здесь.
    В исходнике она присутствует
     
  7. Bers

    Bers Member

    Joined:
    17 May 2010
    Messages:
    78
    Likes Received:
    30
    Reputations:
    26
    Ключевые слова: ThreadPool, WaitHandle, Task, Parallel, CancellationToken, async/await.
    Все это добро прекрасно работает, держит приличную нагрузку и показывает высокий перфоманс.
    Чего нельзя сказать о твоем поделии....

    PHP:
    Uri[] uris;
    CancellationTokenSource cancellation;

    void StartWork()
    {
       
    cancellation = new CancellationTokenSource();

       var 
    tasks = new Task[uris.Length];
       var 
    token cancellation.Token;

       for(var 
    0uris.Lengthi++)
       {
          var 
    uri uris[i];
          
    tasks[i] = Task.Factory.StartNew(() => ProcessUri(uriProcess), token);
       } 

       
    Task.Factory.StartNew(() => 
       {
          try
          {
             
    Task.WaitAll(taskstoken);
             
    Dispatcher.BeginInvoke(new Action(() => MessageBox.Show("completed")));
          }
          catch(
    OperationCanceledException)
          {
             
    Dispatcher.BeginInvoke(new Action(() => MessageBox.Show("cancelled")));
          }
       }); 
    }

    void Cancel()
    {
       
    cancellation.Cancel();
    }

    void LoadUri(Uri uriAction<stringprocessor)
    {
       var 
    request = (HttpWebRequest)WebRequest.Create(uri);
       
    request.CookieContainer = new CookieContainer();
       
    request.AllowAutoRedirect false;
       
    request.Timeout 30000;
       
    request.UserAgent "Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.9.0.19) Gecko/2010031422 Firefox/3.0.19";

       
    using (var response = (HttpWebResponse)request.GetResponse())
       {
          
    response.Cookies request.CookieContainer.GetCookies(request.RequestUri);

          
    using (var stream response.GetResponseStream())
          
    using (var reader = new StreamReader(streamEncoding.GetEncoding(1251)))
          {
             var 
    dataContent reader.ReadToEnd();
             var 
    headers response.Headers.ToString();
             
    processor(headers dataContent);
          }
       }
    }

    void Process(string content)
    {
       
    Console.WriteLine(content);
    }
    Ты, наверно, мне не поверишь, но этот код эффективнее твоего сарая с ручной дрочкой потоков.

    Вообще блин я фигею - люди почему-то бегут писать о том, в чем сами толком не разобрались. Вот у тебя статья про многопоточность - где хоть пара слов о процессорных ресурсах? Где слова про блокировки (я скромно умолчу про lock-free)? Где слова про пул потоков? Где мать их слова про race conditions, про deadlocks?!
    А потом мы удивляемся, почему это некоторые "разработчики" на полном серьезе по каждому чиху плодят по потоку, а потом его еще и грохают руками, получая шанс просрать память? (да, Thread.Abort() вкупе с using() может просрать память, вернее, Dispose() не будет вызван)

    И главное, материалов-то на эту тему хоть жопой ешь, так нет...

    http://www.albahari.com/threading/
    http://www.codeproject.com/Articles/26148/Beginners-Guide-to-Threading-in-NET-Part-1-of-n
    http://code.msdn.microsoft.com/Samples-for-Parallel-b4b76364
    http://parallelpatterns.codeplex.com/

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

    Не, серьезно, я понимаю, что твоя "статья" это суть ответ на один из вечных вопросов античата "я напесал чекер/спамер/бота/ведро-на-колесиках акак сделать чебы было с потоками памагите пжалстаа!!!!1111". Но ответ хреновый. Как и вопрос собственно.
     
    3 people like this.
  8. zoke

    zoke Member

    Joined:
    5 Sep 2010
    Messages:
    90
    Likes Received:
    9
    Reputations:
    5
    Когда я запускал памяти много не тратилось.

    Мой вариант например для чекер подойдёт?


    Вот кстати по смейся
     
    #8 zoke, 7 Mar 2012
    Last edited: 7 Mar 2012
  9. enigma

    enigma Member

    Joined:
    10 Jul 2011
    Messages:
    80
    Likes Received:
    15
    Reputations:
    7
    Само понятие "Форум" или тематического общения, подразумевает подсказки, помощь, обмен опытом, и главное поддержка друг друга.
    Ведь тут у всех один общий интерес.
    Ни для кого не секрет. что уровень знаний у всех разный. Да и вообще каждый умен по своему.

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

    Такое ощущение, что Вы специально сидите на форуме, ждете какую либо тему про сишарп и составляете план очередного оригинального "высера".

    При этом забывается один очень важный нюанс - вы говорите в обстановке полной гласности. Просто совет - будьте помягче. в "высказываниях".

    Человек написал может свою первую статью, пусть даже далекую от совершенства, (это пока, ну все же когда то начинали?) Вы, своими комментариями отбиваете у человека впредь писать что либо, не говоря уже о душевной травме.
    Хотя, что говорить. Читайте и пишите подобное дальше, через заблеванный яичками монитор. наверное это весь смысл пребывания на форуме. ЯЯЯ
     
  10. zoke

    zoke Member

    Joined:
    5 Sep 2010
    Messages:
    90
    Likes Received:
    9
    Reputations:
    5
    ++

    Первая кстати
     
  11. GRRRL Power

    GRRRL Power Elder - Старейшина

    Joined:
    13 Jul 2010
    Messages:
    823
    Likes Received:
    185
    Reputations:
    84
    Ну, лично я бы все-таки посоветовал не писать статьи, когда чувствуешь, что сам в теме не особо разбираешься. Представь - покупаешь ты книгу, читаешь, думаешь, что там все хорошо и правильно... А оказывается, что ее писал какой-то дилетант, и в итоге ты учишься говнокодить по ней.

    Я вот, например, хорошо в плюсах разбираюсь или в php и понимаю, что если что-то на эту тему буду писать, то будет все правильно и так, как должно быть. А вот в DirectX 11 не особо разбираюсь, и если напишу, получится говно. Поэтому никогда не буду никакой отсебятины городить про директ икс, пока как следует тему не изучу. А это небыстро.
     
    1 person likes this.
  12. zoke

    zoke Member

    Joined:
    5 Sep 2010
    Messages:
    90
    Likes Received:
    9
    Reputations:
    5
    Я с тобой полностью согласен.

    Увидев этот урок ( http://zhyk.org/showthread.php?t=351080 ) стало жалко пользователей zhyk'a и для них я сделал этот урок, моему знакому понравился тот урок и он предложил его на ачате опубликовать.
     
  13. Bers

    Bers Member

    Joined:
    17 May 2010
    Messages:
    78
    Likes Received:
    30
    Reputations:
    26
    Ох ты ж блин... Разложу по полочкам

    А проблема в том, что он ничего не знает (либо знает, но не поделился).

    Собрал что? Thread.Start, Thread.Abort, lock без объяснения, как оно вообще работает? И нафиг это собирать?

    Что я и сделал сообщением выше (см. ссылки). А статью писать... да по этой теме статей просто немерено, бери и читай, у одного только Рихтера куча материалов по многопоточности. Нет, блин, пишут отсебятину.

    Черт возьми, человек нифига не разобравшись в теме, бросился писать статьи. Что я, скажи на милость, должен был написать? "Молодец, пиши еще! Очень полезная статья!" что ли? Я, может быть, резко высказался, но справедливо. Не знаешь - не пиши, сначала научись читать. Если я сейчас брошусь писать статью, например, по медицине - меня тоже смешают с говном, и правильно сделают. Ну а если от одного жалкого комментария на каком-то форуме человек душевную травму получит и в петлю полезет - так ему лечиться нужно, это психиатрия.


    А я что-то говорил про память? Тут не в памяти дело, а в числе рабочих потоков. Что произойдет, если ты поставишь например 1000 потоков? А произойдет вот что - начнется мясорубка за процессорные ресурсы. Памяти тоже кстати говоря отожрется по мегабайту за поток - итого гигабайт.

    Есть одна вещь, которую многие почему-то не понимают. Нет такой вещи "многопоточность для чекера", есть только "многопоточность в целом". Есть основы, которые нужно понимать - планирование потоков, синхронизация. Это база, без нее остальные знания бесполезны. Поэтому бесполезны статьи "как сделать многопоточность в чекере" - они всегда рассказывают, как запустить пару потоков, некорректным образом их прибить и только.
    А если есть понимание базовых вещей - тогда сразу станет ясно, что для чекера вообще не нужна явная многопоточность - там просто нечему работать в несколько потоков, основные затраты времени - это ожидание загрузки. Отсюда вывод - достаточно асинхронного кода.
    Только и всего. http://pastebin.com/jWsMspM1 Этот код работает в два раза быстрее тупого многопоточного варианта при меньшем числе рабочих потоков. Для полного счастья можно еще семафором ограничить число одновременных загрузок.

    Я не посмеялся, я обосрался. Не пости такое больше.
    Это кстати как раз то, о чем я говорю - какой-то идиот запостил какое-то говно, а другие это прочли и скопировали. В итоге говна во вселенной стало еще больше, а она у нас и так изрядно засранная.
     
    #13 Bers, 8 Mar 2012
    Last edited: 8 Mar 2012
    2 people like this.
  14. enigma

    enigma Member

    Joined:
    10 Jul 2011
    Messages:
    80
    Likes Received:
    15
    Reputations:
    7
    Вам про Фому, а вы про Ерему. Внятно, вдумчиво, медленно, прочитайте мой пред. пост. И все поймете.
     
    #14 enigma, 8 Mar 2012
    Last edited: 8 Mar 2012
  15. Bers

    Bers Member

    Joined:
    17 May 2010
    Messages:
    78
    Likes Received:
    30
    Reputations:
    26
    Как же я люблю античат! Ответил человеку по пунктам буквально, он мне советует
    Спасибо, я уже прочитал. По слогам. Два раза. И еще один для верности. И даже ответил.

    А вот тебе ответить нечего. Поэтому я и вижу совет "почитать еще раз". Спасибо, до меня доходит с первого раза.

    Будет что сказать - пиши.
     
    1 person likes this.
  16. $asha

    $asha New Member

    Joined:
    6 Jul 2011
    Messages:
    14
    Likes Received:
    4
    Reputations:
    0
    Код ужасен.
     
    1 person likes this.
  17. shadowrun

    shadowrun Banned

    Joined:
    29 Aug 2010
    Messages:
    842
    Likes Received:
    170
    Reputations:
    84
    Синхронизацию...
     
  18. zoke

    zoke Member

    Joined:
    5 Sep 2010
    Messages:
    90
    Likes Received:
    9
    Reputations:
    5
    Спасибо, как удалить тему?
     
  19. $asha

    $asha New Member

    Joined:
    6 Jul 2011
    Messages:
    14
    Likes Received:
    4
    Reputations:
    0
    Нет, не надо. Тут ценные советы есть
     
  20. Kairos

    Kairos Elder - Старейшина

    Joined:
    5 Oct 2009
    Messages:
    37
    Likes Received:
    21
    Reputations:
    21
    Bers, следует заметить, что в твоем коде тоже не все идеально. Например, отмена операции будет происходить дольше, потому что при вызове Cancel все потоки, начавшие выполнять запрос, не остановятся, пока не сделают все до конца. Я не спорю что это правильнее, просто говорю что будет дольше. Также поначалу программа будет медленно разгоняться, поскольку в ThreadPool по умолчанию потоков примерно столько же, сколько ядер. Для данного алгоритма потоков нужно гораздо больше, потому как функция LoadUri в основном ждет данных от сервера (особенно если будет прокси) и в это время можно продолжать делать новые запросы. ThreadPool конечно будет увеличивать кол-во потоков, но всего на 1 в секунду. Естественно, это все настраивается, но иногда проще работать с сырыми потоками.

    Также я рекомендовал бы всем вместо громоздкого HttpWebRequest использовать WebClient. Вот так выглядит функция LoadUri с его использованием:
    PHP:
    void LoadUri(string uriAction<stringprocessor)
    {
        
    using (var web = new WebClient())
        {
            
    string result web.DownloadString(uri);
            
    processor(web.ResponseHeaders.ToString() + result);
        }
    }
    Мало того, у WebClient есть еще и соответствующие асинхронные методы, которые будут работать еще быстрее за счет использования I/O completion ports. Вообще не понимаю, почему все упорно вставляют везде этот HttpWebRequest.
     
    #20 Kairos, 10 Mar 2012
    Last edited: 10 Mar 2012
Thread Status:
Not open for further replies.