Passing channels to threads

Hello, I am a RUST novice who is writing his second RUST program. My first program successfully scanned an 8 channel ADC card at 4000 samples/second in a Raspberry PI and created 24 threads (3 per ADC channel to process (zero crossing, RMS, FFT,...) the streaming sensor data in real time. It worked, amazing! RUST is a very powerful (but frustrating) language.

So my second program was to improve on the clumsy verbose coding of my first program. Since my thread code is similar between channels I have figured out how to clone the code and pass an index to the thread so that it knows what specific actions to perform. My other idea was to create a Vec (below) of the various channels for each thread and pass the specific channel TUPLE to the thread. I have figured out how to do this in the main using the loop index i and the tuple access syntax: txrx[i].0.send(milliseconds).unwrap(); However I can't figure out how to index/pass the specific channel TUPLE to the thread. I have tried dozens of combinations including direct indexing, passing the address of the tuple, copying the TUPLE, extracting the individual send/recv values, nothing works.

I could really use a suggestion! Thanks...

mgehrs


let mut txrx = Vec::new();
//** Create Vec of channels
for _i in 0..NO_CHANNELS{ txrx.push(channel()); }

// ** Main loop
loop {
for i in 0..NO_CHANNELS{
milliseconds = getmillis();
txrx[i].0.send(milliseconds).unwrap();
}
thread::sleep(ten_millis);
} //loop
}

Please read Forum Code Formatting and Syntax Highlighting and edit your prior post by using the edit button under that post.

Many readers of this forum will ignore code snippets that do not follow the posting guidelines for new contributors that are pinned to the top of this forum. Even those who do respond may feel that the lack of following the forum posting guidelines is disrespectful of their time. Thanks. :clap:

2 Likes

Tom,

Thanks for the guidance. I read thru the link you sent, it appears that all my code should be surrounded by the ``` so below: Did I get the correct character? Anything else?

mgehrs

//  **  Main loop
loop {

   for i in 0..NO_CHANNELS{
      milliseconds = getmillis();
      txrx[i].0.send(milliseconds).unwrap();

      }
    thread::sleep(ten_millis);
   }  //loop
}

Yes, that's the correct character. Can you edit the original post? Also if your code actually has indentation that looks like that, you will need to fix it, possibly by running it through rustfmt.

2 Likes

To pass a Sender to a thread, you will need to use std::thread::spawn to create the thread, and then move a single sender to it. You can clone a Sender if you want to pass it to multiple threads, or if its owner (txrx in this case) needs to stay on the main thread.

For example, this code will spawn one thread for each channel. Each spawned thread will send messages on its channel at 10ms intervals:

let mut txrx = Vec::new();
for _i in 0..NO_CHANNELS {
    txrx.push(channel());
}

for i in 0..NO_CHANNELS {
    let tx = txrx[i].0.clone();
    thread::spawn(move || {
        loop {
            let milliseconds = getmillis();
            tx.send(milliseconds).unwrap();
            thread::sleep(ten_millis);
        }
    });
}

See the complete code on Playground.

Note that it's not necessary for each thread to have its own channel. If you want the threads to share a single channel, just clone the same Sender repeatedly:

let (tx, rx) = channel();

for _ in 0..NO_CHANNELS {
    let tx = tx.clone();
    thread::spawn(move || {
        loop {
            let milliseconds = getmillis();
            tx.send(milliseconds).unwrap();
            thread::sleep(ten_millis);
        }
    });
}

Complete example on Playground.

Matt,

Thanks!! I will give that a try…

Mel

Matt,

Unfortunately, cloning doesn't work for my application. I have the main doing sending and the thread doing receiving. I tried to clone the receiver (using the tuple index of txrx[i].1.clone) and received the error message "method not found for receiver". Any other ideas?

Thanks,

Mel

You can't clone a Receiver, since the standard library only has single-consumer channels. Instead, you could keep a collection of senders on the main thread, while moving each receiver to a child thread:

let mut senders = Vec::new();

for i in 0..NO_CHANNELS {
    let (tx, rx) = channel();
    senders.push(tx);
    
    thread::spawn(move || {
        for msg in rx.iter() {
            println!("thread {} received {:?}", i, msg);
        }
    });
}

Complete example on Playground.

1 Like

Crossbeam has mpmc channels. Senders and receivers can be cloned and sent to other threads.

Awesome, thanks, I will give them a try… mgehrs

Matt,

Great idea to separate the two, and only push the tx on the vector. Thanks…

Mel