Как создать локальную переменную потока внутри структуры Rust?

Мне нужна локальная переменная потока, в идеале хранимая в структуре, которая в настоящее время хранит большую часть глобального состояния моей программы.

Первый способ, которым я могу это сделать, - это использовать макрос thread_local!, однако я хотел бы, чтобы этот поток оставался локальным в моей структуре состояния.

Второй способ, которым я могу добиться этого, - это иметь HashMap<Thread,MyThreadLocalData> или подобное между потоками и значением моей локальной переменной (ов) потока. Тогда у меня будет геттер, который использует thread::current для поиска подходящего значения.

Последнее требование, о котором я должен упомянуть, заключается в том, что не все потоки в данной программе создаются кодом Rust, но код Rust может запускаться в любом потоке, поэтому решения должны быть устойчивыми к этому.

Есть ли лучший способ сделать это? Возможно, есть threadId, который позволил бы мне использовать простой Vec вместо HashMap (и / или избежать накладных расходов на хеширование)? Есть ли для этого библиотека?

Другой вариант - изменить параметры каждой функции, которая может использоваться в многопоточном контексте, чтобы принимать как структуру состояния, так и структуру локального состояния потока, однако это не будет легко работать с потоками, созданными не Rust.


person PiRocks    schedule 06.04.2020    source источник


Ответы (2)


Использование локальной переменной потока в вашей структуре можно сделать, поместив ее в блок impl:

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

И доступен с помощью Foo::FOO:

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

Playground
Обратите внимание, однако, что доступ к этому должен должно выполняться с использованием перед ним Foo::, поскольку это не поле, а связанный с ним static.

Это также можно сделать, сохранив ссылку на него:

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.
        }
    }
}
person Optimistic Peach    schedule 06.04.2020
comment
Можно ли сказать, что это работает только до тех пор, пока существует один экземпляр структуры? Есть ли способ разрешить несколько экземпляров структуры или это квалифицируется как отдельный вопрос? - person PiRocks; 07.04.2020
comment
Это будет работать для любого количества экземпляров структуры. Если вам нужен только один экземпляр структуры, тогда ваша структура должна быть помещена в локальную переменную потока. Первый метод не будет зависеть от экземпляра существующей структуры, а второй будет. - person Optimistic Peach; 07.04.2020

Самый простой способ - использовать 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);
person edio    schedule 22.07.2020