2015-10-04 7 views
1

Я пишу систему, в которой у меня есть коллекция Object s, и каждый Object имеет уникальный интегральный идентификатор. Вот как я хотел бы сделать это в C++:Создайте последовательные идентификаторы для каждого экземпляра структуры

class Object { 
public: 
    Object(): id_(nextId_++) { } 

private: 
    int id_; 
    static int nextId_; 
} 

int Object::nextId_ = 1; 

Это, очевидно, не thread_safe, но если бы я хотел, чтобы это было, я мог бы сделать nextId_ в std::atomic_int, или обернуть вокруг семафор выражения nextId_++.

Как бы это сделать (желательно безопасно) ржавчины? Нет статических элементов структуры, а также глобальных переменных переменных. Я всегда мог пройти nextId в функцию new, но эти объекты будут выделены в нескольких местах, и я бы предпочел не прокладывать номер nextId сюда и yon. Мысли?

ответ

4

ни являются глобальным изменяемым переменным SAFE

Вашего C++ примера кажется, что он будет иметь проблемы безопасности потоков, но я не знаю достаточно C++, чтобы быть уверенными.

Однако только несинхронизированный глобальные переменные переменные являются проблемой. Если вы не заботитесь о проблемах кросс-нить, вы можете использовать нить-местные:

use std::cell::Cell; 

#[derive(Debug)] 
struct Monster { 
    id: usize, 
    health: u8, 
} 

thread_local!(static MONSTER_ID: Cell<usize> = Cell::new(0)); 

impl Monster { 
    fn new(health: u8) -> Monster { 
     MONSTER_ID.with(|id| { 
      let id_val = id.get(); 
      id.set(id_val + 1); 
      Monster { id: id_val, health: health } 
     }) 
    } 
} 

fn main() { 
    let gnome = Monster::new(41); 
    let troll = Monster::new(42); 

    println!("gnome {:?}", gnome); 
    println!("troll {:?}", troll); 
} 

Если вы хотите что-то, что лучше работает с несколькими потоками, проверьте lazy_static crate.

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

#[macro_use] 
extern crate lazy_static; 

use std::sync::atomic::{AtomicUsize, Ordering}; 

#[derive(Debug)] 
struct Monster { 
    id: usize, 
    health: u8, 
} 

lazy_static!{ 
    static ref MONSTER_ID: AtomicUsize = AtomicUsize::new(0); 
} 

impl Monster { 
    fn new(health: u8) -> Monster { 
     let id = MONSTER_ID.fetch_add(1, Ordering::SeqCst); 
     Monster { id: id, health: health } 
    } 
} 

fn main() { 
    let gnome = Monster::new(41); 
    let troll = Monster::new(42); 

    println!("gnome {:?}", gnome); 
    println!("troll {:?}", troll); 
} 
5

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

Пример кода: (playpen link)

use std::sync::atomic::{self, AtomicUsize}; 
use std::thread; 

static OBJECT_COUNTER: AtomicUsize = atomic::ATOMIC_USIZE_INIT; 

#[derive(Debug)] 
struct Object(usize); 

impl Object { 
    fn new() -> Self { 
     Object(OBJECT_COUNTER.fetch_add(1, atomic::Ordering::SeqCst)) 
    } 
} 

fn main() { 
    let threads = (0..10).map(|_| thread::spawn(|| Object::new())).collect::<Vec<_>>(); 

    for t in threads { 
     println!("{:?}", t.join().unwrap()); 
    } 
} 
+0

Ах, я всегда забываю, вы можете статически инициализировать атомное, намного чище! – Shepmaster

+1

Я думал, что должен быть конструктивным, а не просто nitpick с комментарием на один раз здесь, на SO. Не уверен, что это удалось. Вот пример. :) – bluss

+0

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

Смежные вопросы