আমি কীভাবে একটি বিশ্বব্যাপী, পরিবর্তনীয় সিঙ্গলটন তৈরি করব?


140

সিস্টেমে কেবলমাত্র একটি ইনস্ট্যান্টেশন সহ স্ট্রাক তৈরি এবং ব্যবহার করার সর্বোত্তম উপায় কী? হ্যাঁ, এটি প্রয়োজনীয়, এটি ওপেনজিএল সাবসিস্টেম এবং এর একাধিক অনুলিপি তৈরি করা এবং এটিকে সর্বত্র ঘুরিয়ে দেওয়া এটিকে মুক্তি দেওয়ার চেয়ে বিভ্রান্তি বাড়িয়ে তুলবে।

সিঙ্গলটন যতটা সম্ভব দক্ষ হওয়া দরকার। স্থিতিশীল অঞ্চলে একটি স্বেচ্ছাচারিতা বস্তু সংরক্ষণ করা সম্ভব বলে মনে হয় না, কারণ এতে Vecএকটি ডেস্ট্রাক্টর সহ একটি রয়েছে । দ্বিতীয় বিকল্পটি হ'ল স্থায়ী অঞ্চলে একটি (অনিরাপদ) পয়েন্টার সঞ্চয় করা, একটি গাদা বরাদ্দ সিঙ্গলটনকে নির্দেশ করে। এটি করার সর্বাধিক সুবিধাজনক এবং নিরাপদতম উপায় কী, সিনট্যাক্স অদৃশ্য রাখার সময়।


1
আপনি কি লক্ষ্য করেছেন যে ওপেনজিএল-র বিদ্যমান জাস্ট বাইন্ডিংগুলি একই সমস্যাটি কীভাবে পরিচালনা করে?
শেপমাস্টার

20
হ্যাঁ, এটি প্রয়োজনীয়, এটি ওপেনজিএল সাবসিস্টেম এবং এর একাধিক অনুলিপি তৈরি করা এবং এটিকে সর্বত্র ঘুরিয়ে দেওয়া এটিকে মুক্তি দেওয়ার চেয়ে বিভ্রান্তি বাড়িয়ে তুলবে। => এটি প্রয়োজনীয় সংজ্ঞা নয় , এটি সম্ভবত সুবিধাজনক (প্রথমে) তবে প্রয়োজনীয় নয়।
ম্যাথিউ এম।

3
হ্যাঁ তোমার একটা কথা আছে যদিও ওপেনজিএল যে কোনও উপায়ে একটি বড় রাষ্ট্রের মেশিন, তাই আমি নিশ্চিত যে আমি এটির কাছাকাছি কোথাও এর ক্লোন নেই, যার ব্যবহারের ফলে কেবল ওপেনএল ত্রুটি হবে।
স্টিভেনকোচেরা

উত্তর:


197

অ-উত্তর উত্তর

সাধারণভাবে গ্লোবাল স্টেট এড়িয়ে চলুন। পরিবর্তে, কোথাও প্রথম দিকে (সম্ভবত ইন main) অবজেক্টটি তৈরি করুন , তারপরে সেই বস্তুর প্রয়োজনীয় পরিবর্তিত স্থানগুলিতে প্রবেশ করুন। এটি আপনার কোডটি প্রায়শই যুক্তিযুক্ত করতে সহজ করে দেয় এবং পিছনের দিকে যতটা বাঁকানো দরকার হয় না।

আপনি বিশ্বব্যাপী পরিবর্তনীয় পরিবর্তনশীল চান তা সিদ্ধান্ত নেওয়ার আগে নিজেকে আয়নায় শক্ত করে দেখুন। বিরল কেসগুলি রয়েছে যেখানে এটি কার্যকর, তাই এটি কীভাবে করা যায় তা জানার জন্য এটি মূল্যবান।

এখনও একটি বানাতে চান ...?

অলস-স্থির ব্যবহার করে

অলস স্ট্যাটিক বাক্স নিজে একটি Singleton তৈরি খাটুনি কিছু কেড়ে নিতে পারে। এখানে একটি বৈশ্বিক পরিবর্তনীয় ভেক্টর রয়েছে:

use lazy_static::lazy_static; // 1.4.0
use std::sync::Mutex;

lazy_static! {
    static ref ARRAY: Mutex<Vec<u8>> = Mutex::new(vec![]);
}

fn do_a_call() {
    ARRAY.lock().unwrap().push(1);
}

fn main() {
    do_a_call();
    do_a_call();
    do_a_call();

    println!("called {}", ARRAY.lock().unwrap().len());
}

যদি আপনি এটি অপসারণ করেন Mutexতবে আপনার কোনও পরিবর্তনীয়তা ছাড়াই গ্লোবাল সিঙ্গলটন রয়েছে।

একাধিক সমবর্তী পাঠকদের অনুমতি দেওয়ার জন্য আপনি এর RwLockপরিবর্তে একটি ব্যবহার করতে পারেন Mutex

একবার_সেল ব্যবহার করে

Once_cell বাক্স নিজে একটি Singleton তৈরি খাটুনি কিছু কেড়ে নিতে পারে। এখানে একটি বৈশ্বিক পরিবর্তনীয় ভেক্টর রয়েছে:

use once_cell::sync::Lazy; // 1.3.1
use std::sync::Mutex;

static ARRAY: Lazy<Mutex<Vec<u8>>> = Lazy::new(|| Mutex::new(vec![]));

fn do_a_call() {
    ARRAY.lock().unwrap().push(1);
}

fn main() {
    do_a_call();
    do_a_call();
    do_a_call();

    println!("called {}", ARRAY.lock().unwrap().len());
}

যদি আপনি এটি অপসারণ করেন Mutexতবে আপনার কোনও পরিবর্তনীয়তা ছাড়াই গ্লোবাল সিঙ্গলটন রয়েছে।

একাধিক সমবর্তী পাঠকদের অনুমতি দেওয়ার জন্য আপনি এর RwLockপরিবর্তে একটি ব্যবহার করতে পারেন Mutex

একটি বিশেষ ক্ষেত্রে: পরমাণু

আপনার যদি কেবল একটি পূর্ণসংখ্যার মান ট্র্যাক করতে হয় তবে আপনি সরাসরি একটি পারমাণবিক ব্যবহার করতে পারেন :

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

static CALL_COUNT: AtomicUsize = AtomicUsize::new(0);

fn do_a_call() {
    CALL_COUNT.fetch_add(1, Ordering::SeqCst);
}

fn main() {
    do_a_call();
    do_a_call();
    do_a_call();

    println!("called {}", CALL_COUNT.load(Ordering::SeqCst));
}

ম্যানুয়াল, নির্ভরতা-মুক্ত বাস্তবায়ন

এটি মরিচা 1.0 বাস্তবায়নstdin থেকে আধুনিক মরিচা জন্য কিছু টুইট সঙ্গে বাস্তবায়ন করা হয়। এর আধুনিক বাস্তবায়নের দিকেও আপনার নজর দেওয়া উচিত io::Lazy। প্রতিটি লাইন কী করে আমি ইনলাইন মন্তব্য করেছি।

use std::sync::{Arc, Mutex, Once};
use std::time::Duration;
use std::{mem, thread};

#[derive(Clone)]
struct SingletonReader {
    // Since we will be used in many threads, we need to protect
    // concurrent access
    inner: Arc<Mutex<u8>>,
}

fn singleton() -> SingletonReader {
    // Initialize it to a null value
    static mut SINGLETON: *const SingletonReader = 0 as *const SingletonReader;
    static ONCE: Once = Once::new();

    unsafe {
        ONCE.call_once(|| {
            // Make it
            let singleton = SingletonReader {
                inner: Arc::new(Mutex::new(0)),
            };

            // Put it in the heap so it can outlive this call
            SINGLETON = mem::transmute(Box::new(singleton));
        });

        // Now we give out a copy of the data that is safe to use concurrently.
        (*SINGLETON).clone()
    }
}

fn main() {
    // Let's use the singleton in a few threads
    let threads: Vec<_> = (0..10)
        .map(|i| {
            thread::spawn(move || {
                thread::sleep(Duration::from_millis(i * 10));
                let s = singleton();
                let mut data = s.inner.lock().unwrap();
                *data = i as u8;
            })
        })
        .collect();

    // And let's check the singleton every so often
    for _ in 0u8..20 {
        thread::sleep(Duration::from_millis(5));

        let s = singleton();
        let data = s.inner.lock().unwrap();
        println!("It is: {}", *data);
    }

    for thread in threads.into_iter() {
        thread.join().unwrap();
    }
}

এটি মুদ্রণ করে:

It is: 0
It is: 1
It is: 1
It is: 2
It is: 2
It is: 3
It is: 3
It is: 4
It is: 4
It is: 5
It is: 5
It is: 6
It is: 6
It is: 7
It is: 7
It is: 8
It is: 8
It is: 9
It is: 9
It is: 9

এই কোডটি মরিচা 1.42.0 এর সাথে সংকলন করে। Stdinবরাদ্দ মেমরি মুক্ত করার চেষ্টা করার জন্য কিছু অস্থির বৈশিষ্ট্য ব্যবহারের বাস্তব বাস্তবায়ন , যা এই কোডটি করে না।

সত্যই, আপনি সম্ভবত SingletonReaderবাস্তবায়ন করতে চাইবেন Derefএবং DerefMutতাই আপনাকে অবজেক্টটিতে ঝুঁকতে হবে এবং নিজেকে এটি লক করতে হবে না।

অলস-স্থির বা একবার_সেল আপনার জন্য যা করে তা এই সমস্ত কাজ।

"গ্লোবাল" এর অর্থ

দয়া করে মনে রাখবেন আপনি যদি এখনও কোন সেগুলিতে অ্যাক্সেস নিয়ন্ত্রণ স্বাভাবিক মরচে scoping এবং মডিউল-স্তরের গোপনীয়তা ব্যবহার করতে পারেন staticবা lazy_staticপরিবর্তনশীল। এর অর্থ হল আপনি এটি কোনও মডিউল বা এমনকি কোনও ফাংশনের অভ্যন্তরে ঘোষণা করতে পারেন এবং এটি সেই মডিউল / ফাংশনের বাইরে অ্যাক্সেসযোগ্য হবে না। এটি অ্যাক্সেস নিয়ন্ত্রণের জন্য ভাল:

use lazy_static::lazy_static; // 1.2.0

fn only_here() {
    lazy_static! {
        static ref NAME: String = String::from("hello, world!");
    }

    println!("{}", &*NAME);
}

fn not_here() {
    println!("{}", &*NAME);
}
error[E0425]: cannot find value `NAME` in this scope
  --> src/lib.rs:12:22
   |
12 |     println!("{}", &*NAME);
   |                      ^^^^ not found in this scope

তবে, চলকটি এখনও বিশ্বব্যাপী যে এর একটি উদাহরণ রয়েছে যা পুরো প্রোগ্রাম জুড়ে রয়েছে।


72
অনেক চিন্তাভাবনার পরেও আমি সিঙ্গেলটনটি ব্যবহার না করার বিষয়ে দৃ convinced়প্রত্যয়ী, এবং পরিবর্তে কোনও বৈশ্বিক পরিবর্তনশীল ব্যবহার না করে এবং সমস্ত কিছু পাস করার জন্য। কোডটি আরও স্ব-ডকুমেন্টিং তৈরি করে যেহেতু রেন্ডারারের অ্যাক্সেসগুলি কী কী তা স্পষ্ট। আমি যদি সিঙ্গলটনে ফিরে যেতে চাই, তবে অন্য উপায়গুলির চেয়ে এটি করা সহজ হবে।
স্টিভেনকোচেরা

4
উত্তরের জন্য ধন্যবাদ, এটি অনেক সাহায্য করেছিল। আমি কেবল ভেবেছিলাম যে অলস_স্ট্যাটিকের জন্য আমি বৈধ ব্যবহারের ক্ষেত্রে কী দেখছি তা বর্ণনা করার জন্য আমি এখানে একটি মন্তব্য করব! আমি এটিকে একটি সি অ্যাপ্লিকেশনের সাথে ইন্টারফেস করতে ব্যবহার করছি যা মডিউলগুলি লোড / আনলোড করার অনুমতি দেয় (ভাগ করা বস্তুগুলি) এবং মরিচা কোডগুলি এই মডিউলগুলির মধ্যে একটি। লোডে গ্লোবাল ব্যবহার করার চেয়ে বেশি বিকল্প আমি দেখতে পাচ্ছি না কারণ মূল ()টির উপর আমার কোনও নিয়ন্ত্রণ নেই এবং মূল অ্যাপ্লিকেশনটি কীভাবে আমার মডিউলটির সাথে ইন্টারফেস করে। আমার মূলত জিনিসগুলির একটি ভেক্টর দরকার যা আমার মোড লোড হওয়ার পরে রানটাইমে যুক্ত হতে পারে।
মাইজেস সিলভা

1
@MoisesSilva সবসময় হতে যাচ্ছে কিছু একটা Singleton প্রয়োজন কারণ, কিন্তু এটা ক্ষেত্রে এটা ব্যবহার করা হয় অনেক এটি ব্যবহার করতে অপ্রয়োজনীয় নয়। আপনার কোডটি না জেনে, এটি সম্ভব যে সি অ্যাপ্লিকেশনটি প্রতিটি মডিউলটিকে "ব্যবহারকারী ডেটা" ফেরত দেওয়ার মঞ্জুরি দেয় void *যা পরে প্রতিটি মডিউলটির পদ্ধতিতে ফেরত দেওয়া হয়। এটি সি কোডের জন্য একটি সাধারণ এক্সটেনশন প্যাটার্ন। যদি অ্যাপ্লিকেশন এটির জন্য অনুমতি দেয় না এবং আপনি এটি পরিবর্তন করতে না পারেন, তবে হ্যাঁ, একটি সিঙ্গলটন একটি ভাল সমাধান হতে পারে।
শেপমাস্টার

3
@ ওয়ারিক আপনি কেন ব্যাখ্যা করবেন যত্ন নেবেন? আমি লোককে এমন কিছু করতে নিরুৎসাহিত করি যা বেশিরভাগ ভাষায় খারাপ ধারণা (এমনকি ওপিও সম্মতি দেয় যে তাদের প্রয়োগের জন্য একটি গ্লোবাল একটি খারাপ পছন্দ ছিল)। এটাই সাধারণ অর্থ। তারপরে আমি কীভাবে এটি করতে পারি তার জন্য দুটি সমাধান দেখাব। আমি কেবল lazy_staticমরিচা 1.24.1 এ উদাহরণটি পরীক্ষা করেছি এবং এটি ঠিক কাজ করে। এখানে external staticকোথাও নেই। আপনি উত্তরটি পুরোপুরি বুঝতে পেরেছেন তা নিশ্চিত করার জন্য আপনার শেষের দিকে কিছু পরীক্ষা করতে হবে।
শেপমাস্টার

1
@ ওয়ারিক যদি আপনার ক্রেট কীভাবে ব্যবহার করতে হয় তার মৌলিক বিষয়গুলির জন্য সহায়তা প্রয়োজন, আমি আপনাকে পরামর্শ দেই যে রাস্ট প্রোগ্রামিং ভাষাটি পুনরায় পড়তে হবে । একটি অনিশ্চয়তার খেলা তৈরির অধ্যায় দেখায় কিভাবে নির্ভরতা যোগ করা হবে।
শেপমাস্টার

0

গ্লোবাল অ্যাক্সেসের জন্য স্পিনলক ব্যবহার করুন ।

#[derive(Default)]
struct ThreadRegistry {
    pub enabled_for_new_threads: bool,
    threads: Option<HashMap<u32, *const Tls>>,
}

impl ThreadRegistry {
    fn threads(&mut self) -> &mut HashMap<u32, *const Tls> {
        self.threads.get_or_insert_with(HashMap::new)
    }
}

static THREAD_REGISTRY: SpinLock<ThreadRegistry> = SpinLock::new(Default::default());

fn func_1() {
    let thread_registry = THREAD_REGISTRY.lock();  // Immutable access
    if thread_registry.enabled_for_new_threads {
    }
}

fn func_2() {
    let mut thread_registry = THREAD_REGISTRY.lock();  // Mutable access
    thread_registry.threads().insert(
        // ...
    );
}

আপনি যদি পরিবর্তনীয় স্থিতি চান (সিঙ্গলটন নয়), আরও বর্ণনার জন্য জাস্টে কী করবেন না দেখুন।

আশা করি এটি সহায়ক।


-1

আমার নিজের সদৃশ প্রশ্নের উত্তর দেওয়া ।

Cargo.toml:

[dependencies]
lazy_static = "1.4.0"

ক্রেট রুট (lib.rs):

#[macro_use]
extern crate lazy_static;

সূচনা (অনিরাপদ ব্লকের প্রয়োজন নেই):

/// EMPTY_ATTACK_TABLE defines an empty attack table, useful for initializing attack tables
pub const EMPTY_ATTACK_TABLE: AttackTable = [
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
];

lazy_static! {
    /// KNIGHT_ATTACK is the attack table of knight
    pub static ref KNIGHT_ATTACK: AttackTable = {
        let mut at = EMPTY_ATTACK_TABLE;
        for sq in 0..BOARD_AREA{
            at[sq] = jump_attack(sq, &KNIGHT_DELTAS, 0);
        }
        at
    };
    ...

সম্পাদনা করুন:

এটি একবার_সেল দ্বারা সমাধান করার জন্য পরিচালিত, যার জন্য ম্যাক্রোর দরকার নেই।

Cargo.toml:

[dependencies]
once_cell = "1.3.1"

square.rs:

use once_cell::sync::Lazy;

...

/// AttackTable type records an attack bitboard for every square of a chess board
pub type AttackTable = [Bitboard; BOARD_AREA];

/// EMPTY_ATTACK_TABLE defines an empty attack table, useful for initializing attack tables
pub const EMPTY_ATTACK_TABLE: AttackTable = [
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
];

/// KNIGHT_ATTACK is the attack table of knight
pub static KNIGHT_ATTACK: Lazy<AttackTable> = Lazy::new(|| {
    let mut at = EMPTY_ATTACK_TABLE;
    for sq in 0..BOARD_AREA {
        at[sq] = jump_attack(sq, &KNIGHT_DELTAS, 0);
    }
    at
});

2
ইতিমধ্যে আলোচিত lazy_staticএবং আরও নতুন উত্তরগুলির তুলনায় এই উত্তরটি নতুন কিছু সরবরাহ করে না once_cell। এসওতে নকল হিসাবে জিনিস চিহ্নিত করার বিষয়টি হ'ল অপ্রয়োজনীয় তথ্য এড়ানো।
শেপমাস্টার
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.