Автор Тема: Эти загадочные ithread  (Прочитано 4882 раз)

0 Пользователей и 1 Гость просматривают эту тему.

Оффлайн 2NetFly

  • Модератор
  • Глобальный модератор
  • Постоялец
  • *****
  • Сообщений: 144
  • +0/-0
  • 0
    • Просмотр профиля
    • http://feotast.net
Эти загадочные ithread
« : 29 Декабря 2004, 02:04:39 »
Есть некоторая задача, которая может быть решена с использованием процессов и SysV IPC или с использованием потоков. Выбор пал на второй вариант. До этого с потоками в перле никогда не работал. Скомпайлили на сервере (FreeBSD 4.9) перл 5.8.6 с поддержкой ithread, начал экспериментировать. Сначала был собран примитивный каркас, код которого приведен ниже.


#!/usr/bin/perl
use strict;
use threads;
use threads::shared;
#use CGI;

use constant MAX_THREAD     => 50;

$| = 1;

my $thread_num : shared = 0;
my $exit = 0;

$SIG{INT} = sub { $exit++ };

while (!$exit) {
    if ($thread_num < MAX_THREAD - 1) {
        print "new thread (thread_num = $thread_num)\\n";
        if (threads->create(\\&thread_do)) {
            lock $thread_num;
            $thread_num++;
        } else {
            die("Can\'t create thread: $@\\n");
        }
    }
}

print "Stopping...\\n";
#1 while $thread_num+;
print "Done\\n";


sub thread_do
{
    threads->self->detach();
    sleep(1 + int(rand(2)));
    {
        lock $thread_num;
        $thread_num--;
    }
}


Сей нехитрый скрипт работал так, как и было задумано. thread_num находился в приделах от 45 до 49, а при остановке выводилось ожидаемое сообщение "A thread exited while 50 threads were running". Проблемы начались после того, как я добавил одну строку – подключение модуля (что именно за модуль – значения не имеет, результат тот же) через use. Скрипт перестал корректно реагировать на INT сигнал, в 90% случаев делая после нескольких сигналов дамп памяти, а в остальных 10% - выводя сообщение с неверным числом потоков (от 50 до 70). При подключении тяжелых модулей, таких как DBI или LWP thread_num не поднималось выше 35 (при ограничении на количество потоков равным 50).

При выполнении некоторого кода в потоке (выполнял в блоке eval и проверял возврат), thread_num в течение нескольких минут показывало 10-15, затем резко возрастало до 49 (скорость была такая, как будто работало не 50, а 5 потоков), а при остановке писало, что работало 60-70 потоков.

Никакого логического объяснения ни одному из этих феноменов я дать не могу. Может кто-нибудь сталкивался с подобными проблемами и решал их?
There Is More Than One Way To Do It (c)

Оффлайн 2NetFly

  • Модератор
  • Глобальный модератор
  • Постоялец
  • *****
  • Сообщений: 144
  • +0/-0
  • 0
    • Просмотр профиля
    • http://feotast.net
Эти загадочные ithread
« Ответ #1 : 29 Декабря 2004, 10:50:19 »
При добавлении минимальной функциональности в поток, появились новые проблемы. Мне нужно было проверить большой список ссылок на наличие обновленных и, если таковые есть, загрузить и сохранить их. Для начала я просто попытался выполнить head запрос:


#!/usr/bin/perl
use strict;
use threads;
use threads::shared;
use LWP::UserAgent;
use HTTP::Request::Common;

use constant MAX_THREAD     => 50;

$| = 1;

my $thread_num : shared = 0;
my $exit = 0;

$SIG{INT} = sub { $exit++ };

while (!$exit) {
    if ($thread_num < MAX_THREAD) {
        print "new thread (thread_num=$thread_num)\\n";
        if (threads->create(\\&thread_do)) {
            lock $thread_num;
            $thread_num++;
        } else {
            die("Can\'t create thread\\n");
        }
    }
}
print "Done\\n";


sub thread_do
{
    threads->self->detach();
    eval {
        my $ua = LWP::UserAgent->new(timeout => 5);
        my $res = $ua->request(HEAD \'http://microsoft.com/\');
        print threads->self->tid() . " $thread_num " . $res->code() . " " .  $res->message() . "\\n";
    };
    print "Thread error $@\\n" if $@;
    {
        lock $thread_num;
        $thread_num--;
    }
}


Ниже приведу части лога с комментариям:
new thread (thread_num=0)
new thread (thread_num=1)
new thread (thread_num=2)
new thread (thread_num=3)
new thread (thread_num=4)
new thread (thread_num=5)
new thread (thread_num=6)
new thread (thread_num=7)
new thread (thread_num=8)
new thread (thread_num=9)
new thread (thread_num=10)
new thread (thread_num=11)
new thread (thread_num=12)
2 12 200 OK
new thread (thread_num=12)
new thread (thread_num=13)
4 13 200 OK
new thread (thread_num=13)
7 13 200 OK
8 12 200 OK
new thread (thread_num=12)
14 12 500 OK
new thread (thread_num=12)
12 12 200 OK
1 11 200 OK
13 10 200 OK
new thread (thread_num=10)
5 10 200 OK
new thread (thread_num=10)
new thread (thread_num=11)
3 11 200 OK
new thread (thread_num=11)
17 11 200 OK

В течение нескольких минут количество потоков судя по thread_num не поднималось выше 13. Затем произошел резкий рост и падение производительности:

new thread (thread_num=12)
new thread (thread_num=13)
new thread (thread_num=14)
new thread (thread_num=15)

new thread (thread_num=46)
new thread (thread_num=47)
new thread (thread_num=48)
new thread (thread_num=49)

После остановки появилось следующее сообщение:

new thread (thread_num=49)
^CDone
A thread exited while 117 threads were running.

Вот такие вот пироги. Как можно объяснить увеличение количества потоков до максимального только после определенного времени? Резкое падение производительности после увеличения количества потоков? И, наконец, 117 потоков вместо ожидаемых 50?
There Is More Than One Way To Do It (c)

Оффлайн 2NetFly

  • Модератор
  • Глобальный модератор
  • Постоялец
  • *****
  • Сообщений: 144
  • +0/-0
  • 0
    • Просмотр профиля
    • http://feotast.net
Эти загадочные ithread
« Ответ #2 : 29 Декабря 2004, 20:10:35 »
Господа, неужели никто не сталкивался с подобной проблемой? Перекопал уже все, что есть по этому поводу: перлдок, соответствующие главы в Кэмэлбуке и Штайне, perl.ithreads – ничего.

Попробовал пойти другим путем и решить эту же задачу с использованием предварительного ветвления (часть когда) и Thread::Queue:

sub thread_do
{
    threads->self->detach();
    my $tid = threads->self->tid();
    while (1) {
        my $ua = LWP::UserAgent->new(timeout => 3);
        my $res = $ua->request(HEAD \'http://microsoft.com/\');
        $result_q->enqueue("$tid;" . $res->code() . ";" . $res->message() . ";");
        # $result_q - экземпляр объекта класса Thread::Queue
       
        print "return $tid\\n";
    }
}


Установил количество потоков равным 50, запустил и успел было обрадоваться: все работало как нужно с надлежащей скоростью. Однако радость моя длилась не долго. Примерно через 15 минут скорость начала падать, а через 20 минут скрипт практически замер. Сделал подсчет итераций цикла для каждого треда и заметил, что после 60-70 итерации треды начали постепенно отмирать (из-за этого и падение скорости), и через некоторое время их оставалось несколько штук.

Попробовал убрать print из thread_do, взял тело цикла в eval и пытался ловить исключительные ситуация – шаманства ни к чему не привели.

Что является причиной и, тем более, как с этим бороться - понять не могу. Надеюсь на вашу помощь.
There Is More Than One Way To Do It (c)

Оффлайн NeoNox

  • Координатор
  • Глобальный модератор
  • Ветеран
  • *****
  • Сообщений: 3012
  • +0/-0
  • 0
    • Просмотр профиля
Эти загадочные ithread
« Ответ #3 : 29 Декабря 2004, 20:38:49 »
Отдебаж количество потомков с помощью
ps -awx | grep test_script_name | wc -l
или перловоым модулем Proc::ProcessTable;
У меня это поведение не воспроизводимо.
perl -v
This is perl, v5.8.5 built for i386-linux-thread-multi
The documentations is your friend

Оффлайн 2NetFly

  • Модератор
  • Глобальный модератор
  • Постоялец
  • *****
  • Сообщений: 144
  • +0/-0
  • 0
    • Просмотр профиля
    • http://feotast.net
Эти загадочные ithread
« Ответ #4 : 29 Декабря 2004, 21:36:39 »
ps не подойдет. Потоки ведь работаю в переделах одного процесса.

А с предварительным формированием тредов вообще неясно в чем дело. Запустил 80 штук, сделали по 800 запросов каждый (проработали чуть более часа), а затем 72 померли. Код упростил, был бы благодарен, если бы кто-нибудь прогнал на своей машине (можно урл на локальный заменить, что трафик не кушал).
There Is More Than One Way To Do It (c)

Оффлайн 2NetFly

  • Модератор
  • Глобальный модератор
  • Постоялец
  • *****
  • Сообщений: 144
  • +0/-0
  • 0
    • Просмотр профиля
    • http://feotast.net
Эти загадочные ithread
« Ответ #5 : 29 Декабря 2004, 21:45:54 »
Сорс:

#!/usr/bin/perl
use strict;

use threads;
use threads::shared;
use LWP::UserAgent;
use HTTP::Request::Common;
use Thread::Queue;

$| = 1;

my $thread_num : shared = 0;
my $max_thread = 50;
my $exit = 0;
my $dump = 0;
my $start_time = 0;

my %tid : shared = ();
my $result_q = Thread::Queue->new();

$SIG{INT} = sub { $exit++ };

$start_time = time();

threads->new(\\&test) for (1..$max_thread);

while (!$exit) {
    for (my $i = 0; $i < $result_q->pending(); $i++) {
        print $result_q->dequeue(), "\\n";
    }
   
    if ($dump++ > 100000) {
        print "Dump\\n";
        dump_tid(\\%tid);
        $dump = 0;
    }
}

print "\\n\\nTime:" . (time() - $start_time) . "\\n";
print "Done\\n";




sub test
{
    threads->self->detach();
    my $tid = threads->self->tid();
    while (1) {        
        my $ua = LWP::UserAgent->new(timeout => 3);
        my $res = $ua->request(HEAD \'http://mail.com/\');
        $result_q->enqueue("$tid;" . $res->code() . ";" . $res->message() . ";");
       
        lock %tid;
        $tid{$tid}++;
    }
}


sub dump_tid
{
    my $tid = shift;
    open (DUMP, "> dump.txt");
    print DUMP "$_ = $tid->{$_}\\n" foreach keys %$tid;
    close DUMP;
}
There Is More Than One Way To Do It (c)

Оффлайн 2NetFly

  • Модератор
  • Глобальный модератор
  • Постоялец
  • *****
  • Сообщений: 144
  • +0/-0
  • 0
    • Просмотр профиля
    • http://feotast.net
Эти загадочные ithread
« Ответ #6 : 29 Декабря 2004, 23:07:25 »
Попробовал на еще одно машине - та же ситуацию. После 10-15 минут рабочими остаются 2-3 треда.
There Is More Than One Way To Do It (c)

Оффлайн NeoNox

  • Координатор
  • Глобальный модератор
  • Ветеран
  • *****
  • Сообщений: 3012
  • +0/-0
  • 0
    • Просмотр профиля
Эти загадочные ithread
« Ответ #7 : 30 Декабря 2004, 12:56:55 »
Цитировать
2NetFly:
ps не подойдет. Потоки ведь работаю в переделах одного процесса.

Запустил бы. У меня количество процессов этого скрипта растет и это видно при запуске ps.
# ps -awx | grep thread | wc -l
     32
The documentations is your friend

Оффлайн 2NetFly

  • Модератор
  • Глобальный модератор
  • Постоялец
  • *****
  • Сообщений: 144
  • +0/-0
  • 0
    • Просмотр профиля
    • http://feotast.net
Эти загадочные ithread
« Ответ #8 : 30 Декабря 2004, 13:37:39 »
Запускал - один процесс. У тебя же линукс? Вполне вероятно, что под линукс какая-то своеобразная реализация самого механизма итредов. У меня ведь фря..
There Is More Than One Way To Do It (c)

Оффлайн NeoNox

  • Координатор
  • Глобальный модератор
  • Ветеран
  • *****
  • Сообщений: 3012
  • +0/-0
  • 0
    • Просмотр профиля
Эти загадочные ithread
« Ответ #9 : 30 Декабря 2004, 13:48:37 »
На фре смогу дома вечером потестить.
Мысль есть такая. Создай простенький скрипт на локальном сервере и направь запросы на него. Пусть он считает количество процессов этого тестового скрипта. Посмотри на их поведение.
The documentations is your friend

Оффлайн 2NetFly

  • Модератор
  • Глобальный модератор
  • Постоялец
  • *****
  • Сообщений: 144
  • +0/-0
  • 0
    • Просмотр профиля
    • http://feotast.net
Эти загадочные ithread
« Ответ #10 : 30 Декабря 2004, 16:21:12 »
Сейчас попробую написать то же само с префорком, sysv ipc и однонаправленным каналом. Может мучения с тредами того не стоят.
There Is More Than One Way To Do It (c)

Оффлайн 2NetFly

  • Модератор
  • Глобальный модератор
  • Постоялец
  • *****
  • Сообщений: 144
  • +0/-0
  • 0
    • Просмотр профиля
    • http://feotast.net
Эти загадочные ithread
« Ответ #11 : 31 Декабря 2004, 13:23:15 »
В общем, о тредах, судя по всему, придется забыть. На перлмонксе мне сказали, что после создания 10-20 тредов в пределах одного приложения их поведение становится непредсказуемым и особо рассчитывать на стабильную работу не стоит. Посему, буду пытаться решить задачу с использованием префорка и SysV. Создам по этому вопросу отдельную тему.
There Is More Than One Way To Do It (c)

 

Sitemap 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28