• perl多线程使用


    原文来自:博客园(华夏35度)http://www.cnblogs.com/zhangchaoyang 作者:Orisun

    <<=========================threads===========================>>

    #!/usr/bin/perl
    use threads ('yield',
                'stack_size' => 64*4096,
                'exit' => 'threads_only',
                'stringify');
     
    sub start_thread {
       my @args = @_;
       print('Thread started: ', join(' ', @args), " ");
    }
     
    ##创建线程的方法
    # my $thr = threads->create('func_name', ...);
    # my $thr = threads->create(sub { ... }, ...);
    # my $thr = threads->create(&func, ...);
    # The "->new()" method is an alias for "->create()".
    my $thr = threads->create('start_thread', 'argument1', 'argument2');     #通过create创建线程。返回线程实例
    $thr->join();                #等待线程结束
    threads->create(sub { print("I am a thread "); })->join();                   #创建一个线程,没有返回值。那这个线程实例如何访问呢?
     
    my $thr2 = async { foreach (@ARGS) { print"$_ "; } };                              #通过async使用匿名子例程创建线程
    $thr2->join();
    if (my $err = $thr2->error()) {
       warn("Thread error: $err ");
    }
     
    # 在隐式的列表环境中调用thread
    my $thr3 = threads->create(sub { return (qw/a b c/); });
    # 在显式的列表环境中调用thread
    my $thr4 = threads->create({'context' => 'list'},
                             sub { return (qw/a b c/); });
    # 由于创建线程时使用的子例程返回的是列表,所以这里的join函数返回的也是列表
    my @results = $thr3->join();
    print "@results ";
    # 把线程从主线程中分离出来
    # $thr->detach();        ##报错:Cannot detach a joined thread,因为$thr已经调用过join()
    $thr4->detach();     ##
    $tid = $thr4->tid();
    print "线程4ID:$tid ";
     
    # Get a thread's object
    $thr6 = threads->self();
    $thr7 = threads->object($tid);
     
    # Get a thread's ID
    $tid = threads->tid();
    $tid = "$thr7";     #根据线程实例获得线程ID
     
    # 给其他线程一个运行的机会
    threads->yield();
    yield();
     
    # 返回未分离的线程列表
    my @threads = threads->list();
    my $thread_count = threads->list();
     
    my @running = threads->list(threads::running);
    my @joinable = threads->list(threads::joinable);
     
    # 判断两个线程是相同
    if ($thr4 == $thr2) {
       print "thread4 equals to thread2. ";
    }
     
    # 管理线程栈大小
    $stack_size = threads->get_stack_size();
    $old_size = threads->set_stack_size(32*4096);
     
    # Create a thread with a specific context and stack size
    my $thr5 = threads->create({ 'context'    => 'list',
                               'stack_size' => 32*4096,
                               'exit'       => 'thread_only' },
                             &start_thread);
     
    # Get thread's context
    my $wantarray = $thr->wantarray();
    print $wantarray," ";
     
    # Check thread's state
    if ($thr5->is_running()) {
       sleep(1);
    }
    if ($thr5->is_joinable()) {
       $thr5->join();
    }
     
    # Send a signal to a thread
    $thr5->kill('SIGUSR1');
     
    # Exit a thread
    threads->exit();                                    

     

    <<=========================Thread========================>>

    $thread = Thread->new(&start_sub)

    $thread = Thread->new(&start_sub,@args)

    start_sub指定线程要执行的子例程,args是传给子例程的参数。

    lock  VARIABLE

    给变量加锁,直到锁超出范围。给变量加锁只影响到lock函数的调用--即一个线程lock var1后,另一个线程再调用lovk var1时线程就会阻塞,但lock  VARIABLE并不影响正常的对变量的访问。

    如果锁往的是一个容器(如哈希或数组),那么其中的每一个元素并没有全部被锁住。比如一个线程中调用lock  @arr,在另一个线程中调用lock $arr[3]时并不会阻塞。

    async  BLOCK;

    async函数创建并返回一个线程实例,该线程要执行的代码全在BLOCK中,这里BLOCK是个匿名子例程,所以其后一定加分号。

    Thread->self

    返回调用Thread->self函数的线程实例。

    Thread->list

    返回non-joined和non-detached线程实例。

    cond_waitLOCKED_VARIALBLE

    cond_signal  LOCKED_VARIALBLE

    cond_broadcast  LOCKED_VARIALBLE

    上面3个函数主要用于线程问同步,都以一个已加锁的变量作为输入参数。当一个线程调用cond_wait后阻塞自己;当一个线程发出cond_broadcast后所有阻塞的线程得救;当一个线程发出cond_signal后只有一个阻塞的线程得救,至于是哪一个由系统内部决定。当然只有LOCKED_VARIALBLE参数相同时才为一组,大家才可以在一起玩同步。

    yield

    把CPU控制权交给另外一个线程,至于是哪个线程依赖于当时的运行环境。

    join

    等待一个线程结束并返回该线程结束时的返回值。

    detach

    分离的线程不允许被join。

    equal

    判断两个线程是否相同。

    tid

    返回线程的tid。tid是递增的,main线程的tid为0。

    done

    判断线程是否已经结束。

    下面这3个函数在5005threads中还可以用,但是在ithreads中已经不可用了。

    lock(&sub)    eval    flags

    <<============================threads::shared============================>>

    默认下数据都是线程私有的,新创建的线程获得已有变量的一份私有拷贝。threads::shared用于在线程之间共享数据结构,可共享的数据类型只有6种,标量数据、数组、散列、以及它们的引用。

    声明共享变量:

    my ($scalar, @array, %hash);             

    share($scalar);             

    share(@array);           

     share(%hash);

    share函数返回共享的值,这通常是一个引用。

    也可以在编译时标记变量为共享变量:

    my ($var, %hash, @array) :shared;

    my ($var, %hash, @array) :shared;
                 my $bork;
     
                 # Storing scalars
                 $var = 1;
                 $hash{'foo'} = 'bar';
                 $array[0] = 1.5;
     
                 # Storing shared refs
                 $var = %hash;
                 $hash{'ary'} = @array;
                 $array[1] = $var;
     
                 # 不能把非共享变量的引赋给一个共享变量,下面这3句是错误的
                 #   $var = $bork;                    # ref of non-shared variable
                 #   $hash{'bork'} = [];               # non-shared array ref
                 #   push(@array, { 'x' => 1 });       # non-shared hash ref

    shared_clone REF

     my $obj = {'foo' => [qw/foo bar baz/]};           

     bless($obj, 'Foo');             

    my cpy=sharedclone(obj); 

    # Object status (i.e., the class an object is blessed into) is also  cloned.           

    print(ref($cpy), " ");         # Outputs 'Foo'

    对于克隆空的数组或散列,下面用法是等价的:

    var = &share([]);   # Same asvar = shared_clone([]);             

    var = &share({});   # Same asvar = shared_clone({});

    is_shared VARIABLE

    判断变量是否为共享变量,如果是则返回变量的内部ID(类似于refaddr函数),如果不是返回undef。

    如果is_shared参数是数组或散列,它并不检查容器中的元素是否为共享变量。如下

    my %hash :shared;
                 if (is_shared(%hash)) {
                     print("\%hash is shared ");
                 }
     
                 $hash{'elem'} = 1;
                 if (is_shared($hash{'elem'})) {                          ##返回undef
                     print("$hash{'elem'} is in a shared hash ");
                 }

     lock VARIABLE

    不能对容器内部的变量进行加锁:

     my %hash :shared;             

    $hash{'foo'} = 'bar';           

     #lock($hash{'foo'});          # Error           

     lock(%hash);                  # Works

    cond_wait VARIABLE

    cond_signal VARIABLE

    cond_broadcast VARIABLE

    这3个函数就不说了,跟threads里的一样。

    cond_wait CONDVAR, LOCKVAR

    当有其他线程signal第一个参数变量CONDVAR时,第二个参数变量LOCKVAR被解锁。

    cond_timedwait VARIABLE, ABS_TIMEOUT       

    cond_timedwait CONDVAR, ABS_TIMEOUT, LOCKVAR

    如果signal未到达,而timeout了,同样会把变量解锁。

    #  创建一个共享的'Foo' object
          my $foo :shared = shared_clone({});
          bless($foo, 'Foo');
     
          # 创建一个共享的 'Bar' object
          my $bar :shared = shared_clone({});
          bless($bar, 'Bar');
     
          # 把'bar' 放到 'foo'里面
          $foo->{'bar'} = $bar;
     
          # 通过线程重新bless the objects
          threads->create(sub {
              # Rebless the outer object
              bless($foo, 'Yin');
     
              # 不能直接 rebless the inner object
              #bless($foo->{'bar'}, 'Yang');
     
              # 重新取回然后 rebless the inner object
              my $obj = $foo->{'bar'};
              bless($obj, 'Yang');
              $foo->{'bar'} = $obj;
     
          })->join();
     
          print(ref($foo),          " ");    # Prints 'Yin'
          print(ref($foo->{'bar'}), " ");    # Prints 'Yang'
          print(ref($bar),          " ");    # Also prints 'Yang'

    注意:如果你还想使用threads,那么你必须在"use threads::shared"之前就"use threads",否则会报告异常。

    如果你把一个数组、散列或它们的引用share以后,那么容器中的元素都会丢失。

    my @arr = qw(foo bar baz);
            share(@arr);
            # @arr is now empty (i.e., == ());
     
            # Create a 'foo' object
            my $foo = { 'data' => 99 };
            bless($foo, 'foo');
     
            # Share the object
            share($foo);        # Contents are now wiped out
            print("ERROR: $foo is empty ")
                if (! exists($foo->{'data'}));

    所以正确的做法是你应该先把一个空的容器share,然后再往里面添加元素。

    <<========================Thread::Semaphore=============================>>

    use Thread::Semaphore;
               my $s = Thread::Semaphore->new();
               $s->down();   # P操作
               # The guarded section is here
               $s->up();     # V操作
     
               # Decrement the semaphore only if it would immediately succeed.
               if ($s->down_nb()) {
                   # 邻界区在此
                   $s->up();
               }
     
               # 强制降低信号量即使他成为负数
               $s->down_force();
     
               # 创建信号量时指定·初始值
               my $s = Thread::Semaphore->new($initial_value);
               $s->down($down_value);
               $s->up($up_value);
               if ($s->down_nb($down_value)) {
                   ...
                   $s->up($up_value);
               }
               $s->down_force($down_value);

    <<===========================Thread::Queue===================================>>

    直接看程序是学习语言的快速方法,注释得很清楚:

    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
    29
    30
    31
    32
    33
    34
    35
    use strict;
               use warnings;
     
               use threads;
               use Thread::Queue;
     
               my $q = Thread::Queue->new();    # 创建一个空的线程队列
     
               # Worker线程
               my $thr = threads->create(sub {
                                           while (my $item $q->dequeue()) {
                                               #处理$item
                                           }
                                        })->detach();
     
               # 向线程发送 work
               $q->enqueue($item1, ...);
     
     
               # 计算队列中有多少项
               my $left $q->pending();
     
               # 非阻塞地出队
               if (defined(my $item $q->dequeue_nb())) {
                   # 处理 $item
               }
     
               # 获取队列中的第2项,注意并没有进行出几队操作
               my $item $q->peek(1);
     
               # 在队头后面插入两个元素
               $q->insert(1, $item1$item2);
     
               # 提取队列中的最后两个元素
               my ($item1$item2) = $q->extract(-2, 2);

    上面代码中出现过的函数我就不介绍了。

    下面的数据类型可以放入队列:
    普通标题数据;

    标量引用;

    数组引用;

    哈希引用;

    以上对象的组合。

    my @ary = qw/foo bar baz/;         

     q->enqueue(@ary);     ##copy the elements 'foo', 'bar' and 'baz' from @ary intoq。

    而对于共享变量,是它的引用进入队列,而没有发生元素的深复制。

    my @ary :shared = qw/foo bar baz/;
               $q->enqueue(@ary);
     
               my $obj = &shared({});
               $$obj{'foo'} = 'bar';
               $$obj{'qux'} = 99;
               bless($obj, 'My::Class');
               $q->enqueue($obj);

    ->new()    ##创建新队列

    ->new(LIST)  ##创建队列时压入元素

    ->enqueue(LIST)    #入队

    ->dequeue()    #从队中取出一个元素

    ->dequeue(COUNT)    #从队中取出COUNT个元素,如果COUNT大于队列长度,则阻塞,下面的方法不会阻塞。

    ->dequeue_nb()       

    ->dequeue_nb(COUNT)

    ->pending()

    返回队列中元素的个数。

    {
          lock($q);   # 销往队列,以防止其他线程中修改和删除队列元素
          my $item = $q->peek();
          if ($item ...) {
              ...
          }
      }
      # 离开块之后,队列变量自动解锁

    ->peek()      #取出队首元素,并没有出险

    ->peek(INDEX)    #取出指定下标的队列元素,INDEX为负数时是从队尾开始数起

    ->insert(INDEX,LIST)    #在指定的位置上插入一组元素,队首元素的INDEX为0

    ->extract()

    ->extract(INDEX)       

    ->extract(INDEX, COUNT)

    删除并返回指定的元素。

    原文来自:博客园(华夏35度)http://www.cnblogs.com/zhangchaoyang 作者:Orisun
  • 相关阅读:
    POI实现Excel导入导出
    2017春季_京东_Java后端研发岗面经
    java中的IO流和多线程
    Java静态代理&动态代理&Cglib代理详解
    Java8新特性——stream流
    Java8新特性——接口默认方法
    Java8新特性——lambda函数式编程
    难题解决:Mycat数据库中间件+Mybatis批量插入数据并返回行记录的所有主键ID
    物料导出FreeMaker模板定义
    Mysql的MyISAM和InnoDB存储引擎的区别
  • 原文地址:https://www.cnblogs.com/lyon2014/p/4351934.html
Copyright © 2020-2023  润新知