challenge 疑似並行処理

数値(たとえば1から10)と、アルファベット(たとえばAからJまで)を順に出力する別々のループ処理を並行に実行させ、共通の出力先に出力する極力シンプルなコードを書いてください。

念のため、実行後、出力先に数値とアルファベットが混ざって出力されている(たとえば、数値がすべて出力されてからアルファベットが続く…というふうになっていない)ことを確認してください。混ざってさえいれば、それぞれ1文字ずつ交互である必要はありませんし、もちろん交互でも構いません。

出力先や出力方法は自由です。標準出力、テキストファイル、コンテナオブジェクト(配列、リスト、コレクション)など使いやすいもので構いません。

例として Squeak Smalltalk でのコードと結果を示します。シンプルなコードなので Smalltalk に馴染みがない人も、おおよその内容は掴めると思います。

1
2
3
4
5
6
7
| out |
out := OrderedCollection new.

[(1 to: 10) do: [:each | out add: each. Processor yield]] fork.
[($A to: $J) do: [:each | out add: each. Processor yield]] forkAndWait.

^out asArray  "=> #(1 $A 2 $B 3 $C 4 $D 5 $E 6 $F 7 $G 8 $H 9 $I 10 $J) "

もちろん可能であれば、疑似である必要はありません。^^;

Posted feedbacks - Nested

Flatten Hidden
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import threading
import time

def p(x):
    print x
    time.sleep(0.001)

threads = [ threading.Thread(target=lambda ls: map(p, ls), args=(ls,))
            for ls in (range(1,11), "ABCDEFGHIJ")]
for th in threads: th.start()
for th in threads: th.join()
スレッド等の並行処理についてはANSI CL仕様外で、
処理系毎に異なりますが、差異を吸収するパッケージもあり、
今回は、その目的にportable-threadsを利用しています。
結果が上手く混ざらないので、それぞれ、短かいsleepをかませてあります。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
(defpackage :doukaku-215 (:use :cl :portable-threads))
(in-package :doukaku-215)

(loop
   :with ans := ()
   :with tasks := (list 
                   (spawn-thread "num" 
                     (lambda () 
                       (dotimes (i 10) 
                         (push i ans)
                         (sleep 0.001))))
                   (spawn-thread "char"
                     (lambda () 
                       (dotimes (i 10) 
                         (push (code-char (+ 65 i)) ans)
                         (sleep 0.001)))))
   ;; wait
   :while (every #'thread-alive-p tasks) :do (sleep 0.05)
   :finally (return ans))

;=> (#\J 9 #\I 8 #\H 7 #\G 6 5 #\F #\E 4 #\D 3 #\C 2 #\B 1 #\A 0)

起動あたりの影響を受けずに混ざって出力されるよう、sleep 1しています。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#!/bin/bash

A(){
  local i
  for i in {1..10}; do
    echo $i
    sleep 1
  done
}

B() {
  local i
  for i in {a..j}; do
    echo $i
    sleep 1
  done
}

A & B

sleepをreturnに変えたら、疑似並行処理なコルーチンみたいなのになりました。

 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
#!/bin/bash

A() {
  while ((i <= 10)); do
    echo $((i++))
    return
  done
  i=DONE
}

CHARS=({a..j})

B() {
  while ((j < ${#CHARS[@]} )); do
    echo ${CHARS[j++]}
    return
  done
  j=DONE
}

doit() {
  local i=1
  local j=0
  
  while [ $i != DONE -a $j != DONE ]; do
    [ $i != DONE ] && A
    [ $j != DONE ] && B
  done
}

doit

さらに変形して継続渡しもどきに。

 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
#!/bin/bash

A() {
  local i=$1
  local cont=$2
  if ((i <= 10)); then
    echo $((i++))
    if [ -n "$cont" ]; then
      $cont "$FUNCNAME $i"
    else
      $FUNCNAME $i
    fi
  else
    [ -n "$cont" ] && $cont
  fi
}

CHARS=({a..j})

B() {
  local i=$1
  local cont=$2
  if ((i < ${#CHARS[@]} )); then
    echo ${CHARS[i++]}
    if [ -n "$cont" ]; then
      $cont "$FUNCNAME $i"
    else
      $FUNCNAME $i
    fi
  else
    [ -n "$cont" ] && $cont
  fi
}

A 1 'B 0'
java.util.concurrent.* ベースで作りました。切り替えは単純に Thread.yield() しようかと思ったんですが、程よく混ざらないので「いっせ~の、せ!」で実行させるために無駄に CyclicBarrier です。
 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
import java.util.concurrent.*;
public class test {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newCachedThreadPool();
        final CyclicBarrier barrier = new CyclicBarrier(2);
        try {
            // その1
            executor.submit(new Callable<Object>(){
                public Object call() throws Exception {
                    for(int i = 1; i <= 10; i++) {
                        barrier.await();
                        System.out.println(i);
                    }
                    return null;
                }
            });
            // その2
            executor.submit(new Callable<Object>(){
                public Object call() throws Exception {
                    for(char c = 'A'; c <= 'J'; c++) {
                        barrier.await();
                        System.out.println(c);
                    }
                    return null;
                }
            });
        }
        finally {
            executor.shutdown();
        }
    }
}

適当な間隔で出力するように実装してみました

 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
36
#include <process.h>
#include <windows.h>
#include <time.h>
#include <stdio.h>

void PrintDigit(void* p_sleepTime)
{
    int i;
    for(i=1; i<=10; i++)
    {
        printf("%d\n", i);
        Sleep(rand() % 300);
    }
}

void PrintAlphabet(void* p_sleepTime)
{
    int i;
    for(i='A'; i<='Z'; i++)
    {
        printf("%c\n", i);
        Sleep(rand() % 100);
    }
}

int main()
{
    HANDLE digit;
    HANDLE alphabet;
    srand(time(NULL));
    digit = (HANDLE)_beginthread(PrintDigit, 0, NULL);
    alphabet = (HANDLE)_beginthread(PrintAlphabet, 0, NULL);
    WaitForSingleObject(digit, INFINITE);
    WaitForSingleObject(alphabet, INFINITE);
    return EXIT_SUCCESS;
}

スレッドをスタートするときに出力する文字列を渡しています。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
using System;
using System.Threading;

class Program
{
    static void Main()
    {
        new Thread(state => Proc(state)).Start("abc");
        new Thread(state => Proc(state)).Start("123");
    }

    static void Proc(object o)
    {
        foreach (var item in (string)o)
        {
            Console.WriteLine(item);
            Thread.Sleep(1000);
        }
    }
}

スレッドセーフのコレクションへ出力しています。 a 1 b 2 c d 3 e f 4 g h 5 i j 6 7 8 9 10

System.ServiceModelへのアセンブリ参照を加えること

 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
36
37
38
39
40
41
using System;
using System.Collections.Generic;
using System.Threading;

class Program
{
    static SynchronizedCollection<object> collection = new SynchronizedCollection<object>();

    static void Main()
    {
        new Thread(state => Proc1(state)).Start();
        new Thread(state => Proc2(state)).Start();

        Thread.Sleep(3000);

        foreach (var item in collection)
        {
            Console.WriteLine(item);
        }
    }

    static void Proc1(object state)
    {
        char[] c = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' };
        foreach (var item in c)
        {
            collection.Add(item);
            Thread.Sleep(100);
        }
    }

    static void Proc2(object state)
    {
        int[] n = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
        foreach (var item in n)
        {
            collection.Add(item);
            Thread.Sleep(200);
        }
    }
}

threadsモジュールを使って実行。 yieldでスレッドが明け渡される場合もあるけど、これくらいのコードだと全部実行されてしまうので sleep 1を入れてます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
use strict;
use threads;

threads->new(sub {
    for (1..10) {
        print $_. "\n";
        sleep 1;
        #threads->yield();
    }
});
threads->new(sub {
    for (my $a = 'A'; $a le 'J'; $a++) {
        print $a. "\n";
        sleep 1;
        #threads->yield();
    }
});

for my $t (threads->list()) {
    $t->join();
}
OpenMPを使います。

gcc 4.2以上なら-fopenmp、iccなら-openmpというコンパイラオプションを付けます。
Visual C++ならオプションでOpenMPを有効にます。

結果:
1 A 2 B 3 C 4 D 5 E 6 F 7 G 8 H 9 I 10 J
 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
#include <iostream>
#include <string>
using namespace std;

void funcA() {
  for (int i=1; i<=10; ++i) {
    #pragma omp critical
    cout<<i<<' ';
  }
}

void funcB() {
  string s="ABCDEFGHIJ";
  for (size_t i=0; i<s.length(); ++i) {
    #pragma omp critical
    cout<<s.at(i)<<' ';
  }
}

int main() {
  #pragma omp parallel num_threads(2)
  {
    #pragma omp sections
    {
      #pragma omp section
      funcA();
      #pragma omp section
      funcB();
    }
  }
}

Thread.passでスレッドを明示的にスイッチさせます。

これで手元では動いたけどスレッドというものの特性上、実行環境に依存しそう。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
[1..10, 'a'..'j'].map{|range|
  Thread.start{
    range.each{|e|
      p e
      Thread.pass
    }
  }
}.each{|thread|
  thread.join
}

Thread.passを使って、一度に全部出力されてしまうのを避けてます。

1
2
3
4
5
6
7
8
t1= Thread.new {
  1.upto(10) { |i| print i; Thread.pass}
}
t2 = Thread.new {
  'a'.upto('j') { |c| print c; Thread.pass}
}
t1.join
t2.join
あくまで擬似並行処理ということで。
  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
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
#include <stdio.h>
#include <stdlib.h>

#define TASK_END ((void*)-1)
/*
void put_num(){
    int i;
    for(i=0;i<10;i++){
        putchar('0'+i);
        fflush(stdout);
    }
}
*/
void put_num(void **local){
    /*ローカル変数定義*/
    struct local_struct{
        int i;
    } *pl;
    if(*local==NULL){
        /*ローカル変数粋確保*/
        *local =malloc(sizeof(struct local_struct));
        pl=*local;

        /*ローカル変数初期化*/
        pl->i=0;
    }else if(*local==TASK_END){
        /*タスク終了済み*/
        return;
    }else
        pl=*local;

    /* タスク内容
       キリのいいところでreturnする。
    */
    for(;pl->i<10;){
        putchar('0'+pl->i);
        fflush(stdout);

        pl->i++;
        //タスクスイッチ
        return;
    }

    //タスク終了処理
    free(pl);
    *local=TASK_END;
}

/*
void put_alpha(){
    int i;
    for(i=0;i<26;i++){
        putchar('A'+i);
        fflush(stdout);
    }
}
*/
void put_alpha(void ** local){
    /*ローカル変数定義*/
    struct local_struct{
        int i;
    } *pl;
    if(*local==NULL){
        /*ローカル変数粋確保*/
        *local =malloc(sizeof(struct local_struct));
        pl=*local;

        /*ローカル変数初期化*/
        pl->i=0;
    }else if(*local==TASK_END){
        /*タスク終了済み*/
        return;
    }else
        pl=*local;

    /* タスク内容
       キリのいいところでreturnする。
    */
    for(;pl->i<26;){
        putchar('A'+pl->i);
        fflush(stdout);

        pl->i++;
        //タスクスイッチ
        return;
    }
    
    //タスク終了処理
    free(pl);
    *local=TASK_END;
}

int main(){
    void* local_n=NULL;
    void* local_a=NULL;
    
    while(local_n!=TASK_END||local_a!=TASK_END){
        put_alpha(&local_a);
        put_num(&local_n);
        put_alpha(&local_a);
    }
    return 0;
}

シンプルにsetIntervalで疑似並列にしてみました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var outputarea;
function print(s) {
  if(!outputarea) {
    outputarea = document.createElement("p");
    document.body.appendChild(outputarea);
  }
  outputarea.innerHTML += s;
}
function f(data, interval) {
  var timer = setInterval(function() {
    if(data && data.length > 0) {
      print(data.shift());
    } else {
      clearInterval(timer);
    }
  }, interval);
}
var data_list = [
  [1,2,3,4,5,6,7,8,9],
  "abcdefghij".split("")
];
for(var i=0;i<data_list.length;i++) {
  f(data_list[i], 100);
}
Scheme では、スレッドについては SRFI-18 と SRFI-21 で決められています。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
(use srfi-42)
(use gauche.threads)
(define (main args)
  (let ((a (make-thread (lambda ()
          (do-ec (: n 1 11)
            (begin (display n) (thread-yield!))))))
        (b (make-thread (lambda ()
          (do-ec (: c #\a #\j)
            (begin (display c) (thread-yield!)))))))
    (thread-start! a)
    (thread-start! b)
    (thread-join! a)
    (thread-join! b)
    (newline))
  0)
継続 (call/cc) を使ってスレッドもどき。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
(define loop
  (lambda (c1 ls)
    (and c1 (pair? ls)
      (begin (display (car ls))
        (loop (call/cc (lambda (c2) (c1 c2))) (cdr ls))))))

(let ((cc (call/cc (lambda (cc)
  (loop cc '(1 2 3 4 5 6 7 8 9 10))))))
  (loop cc '(A B C D E F G H I J))
  (newline))

適当に出力が混ざるようにランダムに thread-yield! しています。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
(use srfi-27)
(use gauche.threads)

(define (my-for-each proc xs)
  (cond ((pair? xs)
         (when (< (random-real) 0.5) (thread-yield!))
         (proc (car xs))
         (my-for-each proc (cdr xs)))))

(begin
  (for-each thread-join!
            (map (lambda (xs)
                   (thread-start! (make-thread (cut my-for-each display xs))))
                 '((1 2 3 4 5 6 7 8 9 10)
                   (a b c d e f g h i j))))
  (newline))
みんな普通に並行処理書いてるような気がするけど、
お題の「擬似」はどこに行ったんだろうと
思わなくもなく。

というわけで、Perl/Tkで擬似並行処理
 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
36
37
38
39
40
41
42
43
use strict;
use warnings;

use Tk;

my $lblvar='';
my $num = 1;
my $al = 'A';

# ウィンドウとラベル
# ラベルに文字が追加されていく
my $mw = Tk::MainWindow->new;
$mw->Label(-textvariable => \$lblvar)->pack(qw/-fill both/);

# Windowが表示されたらタイマ開始
$mw->bind(q/<Expose>/ => sub {
    $mw->after(timergen() , \&append_number);
    $mw->after(timergen() , \&append_alphabet);
});

$mw->MainLoop;

sub timergen
{ int rand 3000 };

sub append
{ $lblvar .= ($lblvar ? ',' : '') . shift; }

sub append_number
{
  return if $num > 10;

  append($num++);
  $mw->after(timergen() , \&append_number);
}

sub append_alphabet
{
  return if $al gt 'J';

  append($al++);
  $mw->after(timergen() , \&append_alphabet);
}
1
2
3
4
5
6
import Data.Char
import Control.Concurrent

main = forkIO f >> g
    where f = mapM_ (\x -> threadDelay 100000 >> putStr (show x)) [1..10]
          g = mapM_ (\x -> threadDelay 100000 >> putChar x) ['A'..'J']

D2.020から言語コアと標準ライブラリが分離されました。 なぜか言語コアのほうに含まれているThreadGroupなるユーティリティクラスを使ってみました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
private import core.thread, std.stdio;

void main() {
    auto threads = new ThreadGroup;
    threads.create({
        foreach(x; "0123456789") {
            write(x);
            Thread.yield;
        }
    });
    threads.create({
        foreach(x; "abcdefghij") {
            write(x);
            Thread.yield;
        }
    });
    threads.joinAll;
}
極力シンプルにと言うことでspawn。
yieldが予約語だったことを忘れてて少し悩んだ。
1
2
3
4
5
6
import scala.concurrent.ops._

object TwoThread extends Application {
  spawn { ('a' to 'j').foreach{ x => print(x + " "); Thread.`yield` } }
  spawn { (  1 to 10 ).foreach{ x => print(x + " "); Thread.`yield` } }
}

	
 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
import System;
import System.Threading;

class Sample {
    static function Main()
    {
        var t = new Sample();
        t.doIt();
    }

    function doIt(){
        var t1 = new Thread(numPrint);
        var t2 = new Thread(charPrint);
        t1.Start();
        t2.Start();
    }

    function numPrint(){
        for(var i=0;i<10;i++){
            print(i+1);
        }
    }
    function charPrint(){
        var data = "abcdefghij"
        for(var i=0;i<10;i++){
            print(data.charAt(i));
        }
    }
}

Sample.Main();
1
Thread.with{[0..9,'a'..'j'].each{x->start{x.each{print it;yield()}}}}
よく混ざるようasyncを二重に入れてみた。
1
2
3
4
5
using Nemerle.Concurrency;

foreach(n in ['0', 'a'])
  async foreach(i in [0..9])
    async Nemerle.IO.print((i + n) :> char)

Thread.yield だと切り替わらなかったのでThread.delayを使いました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
(* ocamlc -thread -o para unix.cma threads.cma para.ml *)

let para printer lst =
  Thread.create
  (fun lst ->
    List.iter
    (fun x ->
      printer x;
      flush stdout;
      Thread.delay 0.1) lst) lst

let _ =
  List.iter
  (fun t -> Thread.join t)
  [ para (fun c -> Printf.printf "%c " c)
    ['A'; 'B'; 'C'; 'D'; 'E'; 'F'; 'G'; 'H'; 'I'; 'J'];
    para (fun i -> Printf.printf "%d " i)
    [1; 2; 3; 4; 5; 6; 7; 8; 9; 10] ]
昔、シューティングゲームを作った時に
スプライトの操作で似たようなことをした記憶がある。

並列実行というよりは、
Windowsのイベント処理みたいなものです
このコードでは、ただのタイマ処理に近いけど、
ポーリングすることが目的だろうということで投下!

ホントに並列実行させるようにしようと思うと
Bのようにプログレス管理して再入可能にしないとだめなのかなー?(状態遷移)

でも、そんなコーディングはしたくない(笑

// gcc -Wall -std=c99 doukaku215.c
 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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <stdio.h>
#include <sys/time.h>

typedef struct tagFRAMEINFO
{
  time_t     next;
  int        state;
  void (*update)(struct tagFRAMEINFO *);
} FRAMEINFO;

void A(struct tagFRAMEINFO *info)
{
    static const int num[] = {1,2,3,4,5,6,7,8,9,10};
    static size_t index = 0;
    printf("%d ", num[index ++]);
    index %= 10;

    info->next = time(NULL) + 1;
}

void B(struct tagFRAMEINFO *info)
{
    switch( info->state ++  )
    {
        default: case 0: break;
        case 1:  printf("A "); break;
        case 2:  printf("B "); break;
        case 3:  printf("C "); break;
        case 4:  printf("D "); break;
        case 5:  printf("E "); break;
        case 6:  printf("F "); break;
        case 7:  printf("G "); break;
        case 8:  printf("H "); break;
        case 9:  printf("I "); break;
        case 10: printf("J "); info->state = 0; break;
    }
    info->next = time(NULL) + 2;
}

int main( int argc, char *argv[] )
{
    FRAMEINFO frame[] = { {0,0,A}, {0,0,B} };
    time_t    now;

    setvbuf(stdout, NULL, _IONBF, 0);
    while( 1 )
    {
        for( size_t n=0; n<(sizeof(frame)/sizeof(*frame)); n++)
        {
            now = time(NULL);
            if( now >= frame[n].next )
            {
                frame[n].update( &frame[n] );
            }
        }
    }

    return 0;
}

タイマーを使って。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
I=0
A=0

数値タイマとはタイマー
これについて
    値は10
    時満ちた時は~
        もし(I<=10)ならば
            "{I} "を継続表示
            I=I+1
        違えば
            停止
    開始

英字タイマとはタイマー
これについて
    値は10
    時満ちた時は~
        もし(A<=10)ならば
            "{CHR(A+65)} "を継続表示
            A=A+1
        違えば
            停止
    開始

boost::thread版です。g++の場合は以下のようにコンパイルしてください。

g++ para.cxx -lboost_thread

 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
#include <string>
#include <iostream>

#include <boost/thread/thread.hpp>

class Proc
{
private:
    std::string m_str;

public:
    Proc(std::string s) : m_str(s) {}
    
    void operator () (void)
    {
        for (int i = 0; i < m_str.size(); ++i)
        {
            std::cout << m_str[i] << std::flush;
        }
    }
};

int main(void)
{
    Proc numbers("0123456789");
    Proc alphabets("ABCDEFGHIJ");
    boost::thread nt(numbers);
    boost::thread at(alphabets);
    nt.join();
    at.join();
    return 0;
}

よく考えてみると、これってマルチコアの場合、本当に並列処理するかもしれないので、お題の「擬似並列処理」からは外れてるかもしれません。

この例は1文字ずつだからいいのですが、
std::coutがスレッドセーフでない可能性を
考慮したコードにしておいたほうがいいと思います。

考慮しました。

 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
#include <string>
#include <cstdlib>
#include <iostream>

#include <boost/thread.hpp>

class Proc
{
private:
    std::string m_str;
    boost::mutex & m_mtx;

public:
    Proc(std::string s, boost::mutex & m) : m_str(s), m_mtx(m) {}
    
    void operator () (void)
    {
        for (int i = 0; i < m_str.size(); ++i)
        {
            usleep(1);
            boost::mutex::scoped_lock lck(m_mtx);
            std::cout << m_str[i] << std::flush;
        }
    }
};

int main(void)
{
    boost::mutex mutex;
    boost::thread numbers(Proc("0123456789", mutex));
    boost::thread alphabets(Proc("ABCDEFGHIJ", mutex));
    numbers.join();
    alphabets.join();
    return 0;
}
以下のようにして実行します。

erlc para.erl 
erl -noshell -s para para -s init stop
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
-module(para).
-export([para/0]).

proc(Pid, []) -> Pid ! void;
proc(Pid, [Chr | Str]) ->
    io:format("~s", [[Chr]]),
    proc(Pid, Str).

para() ->
    Pid = self(),
    spawn(fun() -> proc(Pid, "0123456789") end),
    spawn(fun() -> proc(Pid, "ABCDEFGHIJ") end),
    receive _ -> receive _ -> void end end.

Makefileという名前で保存して、以下のように実行してください。(-jオプションは標準的ではないかもしれないので、もしかするとGNU Make専用かも)

make -j2

 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
all: numbers alphabets

numbers:
    @echo 0
    @echo 1
    @echo 2
    @echo 3
    @echo 4
    @echo 5
    @echo 6
    @echo 7
    @echo 8
    @echo 9

alphabets:
    @echo A
    @echo B
    @echo C
    @echo D
    @echo E
    @echo F
    @echo G
    @echo H
    @echo I
    @echo J
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import stackless

def put_iterator(it):
    for i in it:
        print i
        stackless.schedule()

stackless.tasklet(put_iterator)('ABCDEFGHIJ')
stackless.tasklet(put_iterator)(range(1,11))

stackless.run()
初めての投稿です。宜しくお願いします。 Delphiの投稿がないので。 他の言語に比べると、やはり行数も長いですね。
 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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
program thread;

{$APPTYPE CONSOLE}

uses
  Classes, SyncObjs, SysUtils;

var
  csConsole: TCriticalSection;

procedure SerializeWrite(const s: string);
begin
  csConsole.Enter;
  try
    Write(s);
  finally
    csConsole.Leave;
  end;
  Sleep(1);
end;

type TWriteText = class(TThread)
  private
    FText: string;
  protected
    procedure Execute; override;
  public
    constructor Create(const Text: string);
end;

constructor TWriteText.Create(const Text: string);
begin
  inherited Create(true);
  FText := Text;
  Resume;
end;

procedure TWriteText.Execute;
var
  n: integer;
begin
  for n := 0 to Length(FText) - 1 do
    SerializeWrite(copy(FText, n + 1, 1));
end;

var
  threadWrite0To9: TWriteText;
  threadWriteAtoZ: TWriteText;
begin
  csConsole := TCriticalSection.Create;
  try
    threadWrite0To9 := TWrite0to9.Create('0123456789');
    threadWriteAtoZ := TWriteAtoZ.Create('ABCDEFGHIJ');
    try
      threadWrite0To9.WaitFor;
      threadWriteAtoZ.WaitFor;
    finally
      FreeAndNil(threadWrite0To9);
      FreeAndNil(threadWriteAtoZ);
    end;
  finally
    FreeAndNil(csConsole);
  end;
end.
1-10とA-Jのスマートな書き方がわからなかった。
sleep(0.01)を入れて、交互になるようにしてあります。
そうしないと、2つめのプロセス作る前に
Print終わってしまう気がする。<交互にならない

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#!/usr/bin/env python                                                            
# coding:utf-8

from multiprocessing import Process
import time
def printStr(str):
  for i in str:
    print i
    time.sleep(0.01) # 出力を緩慢に

if __name__ == '__main__':                                                       
  strArr = [[i for i in range(1,10)],
                ['A','B','C','D','E','F','G','H','I','J']]
  for str in strArr:
    p = Process(target = printStr, args=(str,))
    p.start()
ちょっと修正
無駄にめんどくさい事してた・・orz
1
2
3
4
5
if __name__ == '__main__':
  strArr = [range(1,10), 'ABCDEFGHIJ']                                           
  for str in strArr:
    p = Process(target = printStr, args=(str,))
    p.start()

range(1,10)だと1~9までしか出力されないと思う。

spawnを試す。
channelで子プロセスの終了を待つ。

出力
a0b1c2d3e4f5g6h7ij89
 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
36
37
38
39
implement d215;

include "sys.m";
include "draw.m";

d215: module{
    init: fn(ctx: ref Draw->Context, argv: list of string);
};

sys: Sys;

init(ctx: ref Draw->Context, argv: list of string)
{
    i: int;
    ch := chan of int;

    sys = load Sys Sys->PATH;

    spawn print_num(ch);

    for(i = 0; i < 10; i ++){
        sys->print("%c", i + 'a');
    }

    <- ch;
}

print_num(ch: chan of int)
{
    i: int;

    for(i = 0; i < 10; i ++){
        sys->print("%d", i);
    }

    ch <- = 0;

    exit;
}

javascript 1.7 の yield で疑似並列。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
function create_loop(data) {
    for(var i=0,n=data.length;i<n;i++) {
      print(data[i]);
      yield true;
    }
    yield false;
}
var f1 = create_loop([1,2,3,4,5,6,7,8,9,10]);
var f2 = create_loop(['a','b','c','d','e','f','g','h','i','j']);
while(f1.next() & f2.next());

print関数の定義を忘れました。 以下のような関数があるものとしてください。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
var outputarea;
function print(s) {
  if(!outputarea) {
    outputarea = document.createElement("p");
    document.body.appendChild(outputarea);
  }
  outputarea.innerHTML += s;
}
// あるいは
function print(s) {
  console.log(s); // 要firebug
}

Erlang のプレーンな例は、他の方が解答されていましたので、あえて lists ライブラリを積極的に使ったパターンを投稿してみます。 ミソは、終了メッセージの選択受信かな?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
-module(doukaku215).
-author('cooldaemon@gmail.com').
-export([run/0]).

run() ->
  Pid = self(),
  lists:foreach(
    fun (CPid) -> receive {CPid, finish} -> ok end end,
    lists:map(
      fun (Cs) -> spawn(fun () ->
        lists:foreach(fun (C) -> io:fwrite("~s", [[C]]) end, Cs),
        Pid ! {self(), finish}
      end) end,
      lists:map(fun (N) -> lists:seq(N, N+8) end, [$1, $A])
    )
  ).
1
2
3
4
5
6
7
#r "system.servicemodel.dll"

let sc = new System.Collections.Generic.SynchronizedCollection<obj>() in
let inline sc_add x = sc.Add(x); System.Threading.Thread.Sleep(64) in
[ async { Seq.iter sc_add {1..10} }; async { Seq.iter sc_add {'A'..'J'} } ]
|> (Async.Parallel >> Async.Run >> ignore);
sc |> Seq.to_list |> printfn "%A"

こんな風にcurry化って利用するものなのかな??

1
2
3
run = { list, sleep -> list.each{ println it; Thread.sleep(sleep); } }
Thread.start run.curry('a'..'z', 100)
Thread.start run.curry(1..9, 200)

makecontext()とかでお手軽にコンテキストスイッチ。

 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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include <ucontext.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>


#define STACK_NUM  2
#define ITEM_NUM  10
#define ITEM_SIZE  3

#define handle_error(msg) \
  do { perror(msg); exit(EXIT_FAILURE); } while (0)

static struct {
  int pos;
  struct {
    ucontext_t ctx;
    char stack[16384];
  } proc[STACK_NUM];
} uctx;


static void next_uctx(void)
{
  int old_uctx_pos = uctx.pos;

  uctx.pos = (uctx.pos + 1) % STACK_NUM;

  if (swapcontext(&uctx.proc[old_uctx_pos].ctx, &uctx.proc[uctx.pos].ctx) == -1)
    handle_error("swapcontext");
}

static void func(char list[ITEM_NUM][ITEM_SIZE])
{
  int i;

  for(i = 0; i < ITEM_NUM; i++) {
    printf("%s ", list[i]);
    usleep(100);
    next_uctx();
  }
}

int main(int argc, char *argv[])
{
  int i;
  char lists[STACK_NUM][ITEM_NUM][ITEM_SIZE] = {
    { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" },
    { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J" },
  };
  ucontext_t uctx_main;

  for(i = 0; i < STACK_NUM; i++) {
    ucontext_t *ucp = &uctx.proc[i].ctx;

    if (getcontext(ucp) == -1)
      handle_error("getcontext");

    ucp->uc_stack.ss_sp = uctx.proc[i].stack;
    ucp->uc_stack.ss_size = sizeof(uctx.proc[0].stack);
    ucp->uc_link = (i >= STACK_NUM - 1) ? &uctx_main: &uctx.proc[i + 1].ctx;
    makecontext(ucp, (void (*)(void))func, 1, lists[i]);
  }

  if (swapcontext(&uctx_main, &uctx.proc[0].ctx) == -1)
    handle_error("swapcontext");

  exit(EXIT_SUCCESS);
}

Clojure です、トランザクション内で更新をかけるんですね。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
(import '(java.util.concurrent Executors))

(defn- par
  [& tasks]
  (let [exe (.newCachedThreadPool Executors)]
    (try
     (.invokeAll exe tasks)
     (finally (.shutdown exe)))))

(let [r (ref '())]
  (par
   (fn []
     (dotimes n 10
       (dosync (alter r #(cons (inc n) %)))
       (.sleep Thread 1)))
   (fn []
     (dotimes n 10
       (dosync (alter r #(cons (Character. (char (+ 64 (inc n)))) %)))
       (.sleep Thread 1))))
  @r)
;=>(\J 10 \I 9 \H 8 \G 7 \F 6 \E 5 \D 4 \C 3 \B 2 \A 1)

新しいリリースに追従しました。

static メソッドの呼び方、バインディングの記法を修正しました。

# conj を使うのが Clojure っぽいのかしらん。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
(defn- par
  [& tasks]
  (let [exe (java.util.concurrent.Executors/newCachedThreadPool)]
    (try
     (.invokeAll exe tasks)    
     (finally (.shutdown exe)))))

(let [r (ref [])]
  (par
   (fn []
     (dotimes [n 10]
       (dosync (alter r #(conj % (inc n))))
       (Thread/sleep 1)))
   (fn []
     (dotimes [n 10]
       (dosync (alter r #(conj % (Character. (char (+ 64 (inc n)))))))
       (Thread/sleep 1))))
  @r)
;⇒ [1 \A 2 \B 3 \C 4 \D 5 \E 6 \F 7 \G 8 \H 9 \I 10 \J]
pthreadに興味がわいたのでテスト。
gcc -lpthreadでコンパイル。
putcはスレッドセーフという前提です。

出力:
a01bc23de45fg67hi89j
 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
36
37
38
#include <stdio.h>
#include <pthread.h>

void *thread_alpha(void *arg);

int main( int argc, char ** argv )
{
    pthread_t pt;
    int i;
    int err;

    err =  pthread_create( &pt, NULL, &thread_alpha, NULL);
    if(err){
        return -1;
    }

    for(i = 0; i < 10; i ++){
        putc('0' + i, stdout);
        usleep(1);
    }

    /* スレッドの終了を待つ */
    pthread_join( pt, NULL);

    return 0;
}

void *thread_alpha(void *arg)
{
    int i;

    for(i = 0; i < 10; i ++){
        putc_unlocked('a' + i, stdout);
        usleep(1);
    }

    pthread_exit(0);
}

いちおう擬似並行処理といえるかな。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
def f(s):
  for i in s:
   print i,
   yield i

def run(ths):
  while ths:
    for th in ths:
      try:
        th.next()
      except StopIteration:
        ths.remove(th)

run([f('ABCDEFGHIJ'), f(range(1,11))])
スレッドを使わない擬似並行処理です。
一行Pythonで119 bytes.

一行にするとループやgeneratorが使えないため、
lambdaのリストを巡回することで代用しています。
1
2
import sys;F=lambda x:(lambda:sys.stdout.write(chr(x)));
[[p()for p in q]for q in [(F(i+48),F(i+65))for i in range(10)]]

スレッドつくって画面表示。最後のThread.delayは、これが無いとすぐに実行が終了してしまうので、待ちを入れる意味です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
open Format
  
let spawn_loop f x =
  let rec forever f x = 
    let v = f x in forever f v
  in
  ignore (Thread.create (forever f) x)
    
let _ =
  spawn_loop (fun () ->
    List.iter (fprintf std_formatter "%d@?")  [1; 2; 3; 4; 5; 6; 7; 8; 9; 10;]) ();
  spawn_loop (fun () ->
    List.iter (fprintf std_formatter "%c@?")  ['A'; 'B'; 'C'; 'D'; 'E'; 'F'; 'G'; 'H'; 'I'; 'J']) ();
  Thread.delay 10.

スレッド2つにしてみました。

 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
implement Channel;

include "sys.m";
    sys: Sys;
include "draw.m";

Channel: module
{
    init: fn(ctxt: ref Draw->Context, argv: list of string);
};

init(nil: ref Draw->Context, nil: list of string)
{
    sys = load Sys Sys->PATH;
    c1 := chan of int;
    c2 := chan of int;

    spawn task('A', 10, c1);
    spawn task('0', 10, c2);
    (<- c1, <- c2);
    sys->print("\n");
}

task(bp, n: int, c: chan of int)
{
    for(i := 0; i < n; i++)
        sys->print("%c", bp+i);
    c <- = 0;
}

初チャレンジ。 Lua のコルーチンで for ループを素朴に使っています。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function mkfunc (printfunc)
  return function ()
    for v = 1, 10 do
      printfunc (v)
      coroutine.yield ()
    end
  end
end

function printalpha (n)
  print (string.char (n + string.byte ("A") - 1))
end

function costat (c)
  return coroutine.status (c) == "suspended"
end

local c, d = coroutine.create (mkfunc (print)),
             coroutine.create (mkfunc (printalpha))

while costat (c) or costat (d) do
  coroutine.resume (c)
  coroutine.resume (d)
end

Executorを使いたかったので。

1
2
3
exec = java.util.concurrent.Executors.newCachedThreadPool()
[1..9,'a'..'z'].each{r->exec.submit({r.each{println it;Thread.yield()}})}
exec.shutdown()

普通にfork(2)で子プロセスを作る例が無かったので,書いてみました.

子プロセスの実行開始のタイミングをそろえるために,signalを使っています.また,ほどよく出力が混ざるように適宜usleepしています.

 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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>

#define ITEMS_MAX 10
#define CHILD_MAX 2

#ifndef false
# define false 0
#endif
#ifndef true
# define true !false
#endif

static int is_activated = false;

static const char *table[][ITEMS_MAX + 1] = {
  { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" },
  { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J"  },
};

int child_proc(pid_t, const char **);
void child_activate(int);

int
main(int argc, void **argv)
{
    int i;
    pid_t children[CHILD_MAX];
    memset(children, -1, sizeof(children));

    /* create child processes */
    for (i = 0; i < CHILD_MAX; i++) {
        if ((children[i] = fork()) == -1)
            exit(EXIT_FAILURE);
        else if (children[i] == 0)
            _exit(child_proc(getpid(), table[i]));
    }

    /* activate all of children by sending signal */
    usleep(100000);
    for (i = 0; i < CHILD_MAX; i++)
        kill(children[i], SIGUSR1);

    /* wait until all child processes finish */
    for (i = 0; i < CHILD_MAX; i++) {
        int status;
        if (wait(&status) == -1)
            exit(EXIT_FAILURE);
    }

    exit(EXIT_SUCCESS);
}

void
child_activate(int signum)
{
    is_activated = true;
}

int
child_proc(pid_t my_pid, const char **table)
{
    int c;
    struct sigaction act;

    /* waiting signal from the parent */
    memset(&act, 0, sizeof(act));
    act.sa_handler = child_activate;
    if (sigaction(SIGUSR1, &act, NULL) != 0)
        return EXIT_FAILURE;

    sleep(60);
    if (!is_activated)
        return EXIT_FAILURE;    /* timed out */

    /* process something */
    for (c = 0; c < ITEMS_MAX; c++) {
        printf("%s ", table[c]);
        fflush(stdout);
        usleep(10000);
    }
    printf("\n");

    return EXIT_SUCCESS;
}

疑似ではなくちゃんと並列になってる……はず.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
component doukaku215
  import List.{...}
  export Executable

  run(args: String...): () = do

    do
      for i <- 0#10 do
        t = spawn print i ", "
        t.wait()
      end
    also do
      for c <- <|"A", "B", "C", "D", "E", "F", "G", "H", "I", "J"|> do
        t = spawn print c ", "
        t.wait()
      end
    end
    print "\n"

  end
end
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
    randomize
    sdim buf
    cntAlph = 0
    cntDig  = 0
    
    repeat 26
        buf += cntDig
        buf += strf("%c", 'A'+ cntAlph)
        cntDig  ++
        cntAlph ++
    loop
    mes buf

Clojure1.1.0

1
2
3
4
5
6
7
8
9
(def out (ref []))

(defn put [sq]
  (doall (map #(dosync (Thread/sleep 10) (alter out conj %)) sq)))

(pcalls #(put [1 2 3 4 5 6 7 8 9 10])
        #(put [:a :b :c :d :e :f :g :h :i :j]))

(println @out)

Index

Feed

Other

Link

Pathtraq

loading...