3

Question - if you have a single system but have multiple arbiters in it running, is it STILL a single threaded event loop?

Reading through the Actix book - https://actix.rs/book/actix/sec-6-sync-arbiter.html

When you normally run Actors, there are multiple Actors running on the System's Arbiter thread, using its event loop

And also from https://actix.rs/book/actix/sec-5-arbiter.html

While it only uses one thread, it uses the very efficient event loop pattern which works well for asynchronous events. To handle synchronous, CPU-bound tasks, it's better to avoid blocking the event loop and instead offload the computation to other threads. For this usecase, read the next section and consider using SyncArbiter.

Question Repeated - if you have a single system but have multiple arbiters in it running, is it STILL a single threaded event loop?

Example

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();

Does this run in a single thread?

When I log it using log4rs I see the following

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

Given that it is system:0 - does this mean it is the same thread just using different arbiters?

Do I need to run multiple System::new in order to achieve proper multi threading in actix?

kmdreko
  • 42,554
  • 6
  • 57
  • 106
alwinc
  • 1,317
  • 3
  • 14
  • 21

1 Answers1

2

Citing the exact same book:

To use Actix in a concurrent way, you can spin up multiple Arbiters using Arbiter::new, ArbiterBuilder, or Arbiter::start.

Also in the docs for Arbiter::new()

Spawn a new Arbiter thread and start its event loop.

So you can definitely have a multi-threaded event loop.

Below is an example. During execution use a process monitoring software to see the number of threads (e.g. Activity Monitor for MacOS, Process Explorer for Windows, htop for 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(&mut self, msg: Fibonacci, context: &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();
}
meph
  • 662
  • 3
  • 10