Система Actix, несколько арбитров = сколько потоков

#multithreading #rust-actix

Вопрос:

Вопрос — если у вас одна система, но в ней работает несколько арбитров, является ли она ПО-прежнему однопоточным циклом событий?

Чтение книги Actix — https://actix.rs/book/actix/sec-6-sync-arbiter.html

Когда вы обычно запускаете Актеров, в потоке Арбитра Системы работает несколько Акторов, использующих цикл событий

А также от https://actix.rs/book/actix/sec-5-arbiter.html

Хотя он использует только один поток, он использует очень эффективный шаблон цикла событий, который хорошо работает для асинхронных событий. Для обработки синхронных задач, связанных с процессором, лучше избегать блокировки цикла событий и вместо этого загружать вычисления в другие потоки. Для этого случая использования прочитайте следующий раздел и рассмотрите возможность использования SyncArbiter.

Повторный вопрос — если у вас есть одна система, но в ней работает несколько арбитров, по-ПРЕЖНЕМУ ли это однопоточный цикл событий?

Пример

 let sys = System::new();
sys.block_on( async move {
    let arbiter1 = Arbiter::new(); // one arbiter here
    arbiter1.spawn( SOME ACTOR);
    let arbiter2 = Arbiter::new(); // another arbiter here
    arbiter2.spanw( SOME OTHER ACTOR);
});

sys.run().unwrap();
 

Выполняется ли это в одном потоке?

Когда я регистрирую его с помощью log4rs, я вижу следующее

 [actix-rt|system:0|arbiter:0]
[actix-rt|system:0|arbiter:1]
 

Учитывая, что это система:0 — означает ли это, что это один и тот же поток, просто использующий разных арбитров?

Нужно ли запускать несколько System::new программ, чтобы обеспечить правильную многопоточность в actix?

Комментарии:

1. У меня та же дилемма. Вам удалось выяснить, является ли он многопоточным?

Ответ №1:

Цитируя точно такую же книгу:

Чтобы использовать Actix одновременно, вы можете запустить несколько арбитров с помощью Arbiter::new, ArbiterBuilder или Arbiter::start.

Также в документах для Arbiter::new()

Создайте новый поток арбитра и запустите его цикл событий.

Таким образом, у вас определенно может быть многопоточный цикл событий.

Ниже приведен пример. Во время выполнения используйте программное обеспечение для мониторинга процессов, чтобы увидеть количество потоков (например, Монитор активности для macOS, Проводник процессов для Windows, htop для Linux).

 extern crate actix;
use actix::prelude::*;

struct Fibonacci(pub u32);


struct SomeActor;

impl Actor for SomeActor {
    type Context = Context<Self>;
}

impl Message for Fibonacci{
    type Result = Result<u64, ()>;
}

impl Handler<Fibonacci> for SomeActor {
    type Result = Result<u64, ()>;

    fn handle(amp;mut self, msg: Fibonacci, context: amp;mut Self::Context) -> Self::Result {
        println!("working on fib({})", msg.0);
    let mut sum=0;
    if msg.0 == 0 {
        Err(())
    } else if msg.0 == 1 {
        Ok(1)
    } else {

        for j in 1..1000000 {
            let mut i = 0;
            sum = 0;
            let mut last = 0;
            let mut curr = 1;
            while i < msg.0 - 1 {
                sum = last   curr;
                last = curr;
                curr = sum;
                i  = 1;
            }
        }
        Ok(sum)
    }
}
}

fn main() {
let sys = System::new();

let a1 = Arbiter::new();
let a2 = Arbiter::new();
let a3 = Arbiter::new();

let execution1 = async {
    println!("exec 1 created");
    let sa = SomeActor {}.start();
    for n in 2..80 {
        sa.do_send(Fibonacci(n));
    }
};

let execution2 = async {
    println!("exec 2 created");
    let sa = SomeActor {}.start();
    for n in 2..80 {
        sa.do_send(Fibonacci(n));
    }
};

let execution3 = async {
    println!("exec 3 created");
    let sa = SomeActor {}.start();
    for n in 2..80 {
        sa.do_send(Fibonacci(n));
    }
};


a1.spawn(execution1);
a2.spawn(execution2);
a3.spawn(execution3);

sys.run();
}