Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a thread local variable inside of a Rust struct?

I need a thread local variable, ideally stored in a struct which currently stores most of my program's global state.

The first way I can see to do this is to use the thread_local! macro, however I would like to keep this thread local within my state struct.

The second way I can see to achieve this is to have a HashMap<Thread,MyThreadLocalData> or similar between threads and the value of my thread local variable(s). I would then have a getter which uses thread::current to lookup the appropriate value.

One last requirement which I should mention is that not all threads in the given program are created by Rust code, but Rust code may be run on any thread, so solutions should be robust to this.

Is there a best practice way of doing this? Perhaps there is a threadId that would allow me to use a simple Vec instead of a HashMap (and/or avoid hashing overhead)? Is there a library for this?

Another option would be to modify the parameters of every function that could be used in a multithreaded context to take both a state struct and a threadlocal state struct, however this would not easily work with threads not created by Rust.

like image 251
PiRocks Avatar asked Apr 06 '20 23:04

PiRocks


People also ask

How do I create a local thread variable?

Creating a ThreadLocal variable The variable of ThreadLocal can be created using the following code: private ThreadLocal threadLocal = new ThreadLocal();

What is ThreadLocal rust?

This library provides the ThreadLocal type which allows a separate copy of an object to be used for each thread. This allows for per-object thread-local storage, unlike the standard library's thread_local! macro which only allows static thread-local storage. Per-thread objects are not destroyed when a thread exits.

Can threads access local variables?

But: No, threads do not share real local variables.

How local thread variables are created and accessed?

The ThreadLocal class is used to create thread local variables which can only be read and written by the same thread. For example, if two threads are accessing code having reference to same threadLocal variable then each thread will not see any modification to threadLocal variable done by other thread.


Video Answer


2 Answers

Using a thread local variable in your struct can be done by placing it in an impl block:

use std::cell::RefCell;

struct Foo;
impl Foo {
    thread_local! {
        // Could add pub to make it public to whatever Foo already is public to.
        static FOO: RefCell<usize> = RefCell::new(0);
    }
}

And is accessible using Foo::FOO:

Foo::FOO.with(|x| println!("{:?}", x));

Playground
Note, however, that accessing this must be done using Foo:: preceding it, since it's not a field, but instead an associated static.

It could also be done by storing a reference to it:

use std::cell::RefCell;
use std::thread::LocalKey;

thread_local! {
    // Note lack of pub
    static FOO: RefCell<usize> = RefCell::new(0);
}
struct Bar {
    // Visibility here changes what can see `foo`.
    foo: &'static LocalKey<RefCell<usize>>,
    // Rest of your data.
}
impl Bar {
    fn constructor() -> Self {
        Self {
            foo: &FOO,
            // Rest of your data.
        }
    }
}
like image 57
Optimistic Peach Avatar answered Nov 09 '22 20:11

Optimistic Peach


The easiest way is to use the thread_local crate:

struct Foo {
    thread_counter: ThreadLocal<AtomicU64>,
}

impl Foo {
    fn new() -> Foo {
        Foo {
            thread_counter: ThreadLocal::new(),
        }
    }
}
let foo = Foo::new();
let count = foo
    .thread_counter
    .get_or(|| AtomicU64::new(START_VALUE))
    .fetch_add(1, Ordering::Relaxed);
like image 32
edio Avatar answered Nov 09 '22 22:11

edio