Thread Safety in C

前端 未结 9 1926
余生分开走
余生分开走 2021-02-02 01:01

imagine I write a library in C. Further, imagine this library to be used from a multi-threaded environment. How do I make it thread-safe? More specific: How do

相关标签:
9条回答
  • 2021-02-02 01:08

    If your goal is to be compatible on unix-like operating systems, I would use POSIX threading.

    That being said, if you want to support windows as well, you'll need to have two code paths for this - pthreads on unix and Windows threads on Windows. It's fairly easy to just make your own "thread library" to wrap these.

    There are quite a few that do this (like OpenThreads), but most of them I've used are C++, not C.

    0 讨论(0)
  • 2021-02-02 01:22

    You can create wrappers with #ifdef. It's really the best you can do. (Or you can use a third party library to do this).

    I'll show how I did it as an example for windows and linux. It's in C++ and not C but again it's just an example:

    #ifdef WIN32
    typedef HANDLE thread_t;
    typedef unsigned ThreadEntryFunction;
    #define thread __declspec(thread)
    
    class Mutex : NoCopyAssign
    {
    public:
        Mutex() { InitializeCriticalSection(&mActual); }
        ~Mutex() { DeleteCriticalSection(&mActual); }
        void Lock() { EnterCriticalSection(&mActual); }
        void Unlock() { LeaveCriticalSection(&mActual); }
    private:
        CRITICAL_SECTION mActual;
    };
    
    class ThreadEvent : NoCopyAssign
    {
    public:
        ThreadEvent() { Actual = CreateEvent(NULL, false, false, NULL); }
        ~ThreadEvent() { CloseHandle(Actual); }
        void Send() { SetEvent(Actual); }
    
        HANDLE Actual;
    };
    #else
    typedef pthread_t thread_t;
    typedef void *ThreadEntryFunction;
    #define thread __thread
    extern pthread_mutexattr_t MutexAttributeRecursive;
    
    class Mutex : NoCopyAssign
    {
    public:
        Mutex() { pthread_mutex_init(&mActual, &MutexAttributeRecursive); }
        ~Mutex() { pthread_mutex_destroy(&mActual); }
        void Lock() { pthread_mutex_lock(&mActual); }
        void Unlock() { pthread_mutex_unlock(&mActual); }
    private:
        pthread_mutex_t mActual;
    };
    
    class ThreadEvent : NoCopyAssign
    {
    public:
        ThreadEvent() { pthread_cond_init(&mActual, NULL); }
        ~ThreadEvent() { pthread_cond_destroy(&mActual); }
    
        void Send() { pthread_cond_signal(&mActual); }
    private:
        pthread_cond_t mActual;
    };
    
    inline thread_t GetCurrentThread() { return pthread_self(); }
    #endif
    
    /* Allows for easy mutex locking */
    class MutexLock : NoAssign
    {
    public:
        MutexLock(Mutex &m) : mMutex(m) { mMutex.Lock(); }
        ~MutexLock() { mMutex.Unlock(); }
    private:
        Mutex &mMutex;
    };
    
    0 讨论(0)
  • 2021-02-02 01:22

    You also should avoid static and global variables that can be modified avoiding synchronization code all over your module

    0 讨论(0)
  • 2021-02-02 01:24

    You will need to use your OS's threading library. On Posix, that will usually be pthreads and you'll want pthread_mutex_lock.

    Windows has it's own threading library and you'll want to look at either critical sections or CreateMutex. Critical sections are more optimized but are limited to a single process and you can't use them in WaitForMultipleObjects.

    0 讨论(0)
  • 2021-02-02 01:27

    You have two main options:

    1) You specify which multi-threaded environment your library is thread-safe in, and use the synchronisation functions of that environment.

    2) You specify that your library is not thread-safe. If your caller wants to use it in a multi-threaded environment, then it's their responsibility to make it thread-safe, by using external synchronisation if necessary to serialise all calls to your library. If your library uses handles and doesn't need any global state, this might for instance mean that if they have a handle they only use in a single thread, then they don't need any synchronisation on that handle, because it's automatically serialised.

    Obviously you can take a multi-pack approach to (1), and use compile-time constants to support all the environments you know about.

    You could also use a callback architecture, link-time dependency, or macros, to let your caller tell you how to synchronise. This is kind of a mixture of (1) and (2).

    But there's no such thing as a standard multi-threaded environment, so it's pretty much impossible to write self-contained code that is thread-safe everywhere unless it's completely stateless (that is, the functions are all side-effect free). Even then you have to interpret "side-effect" liberally, since of course the C standard does not define which library functions are thread-safe. It's a bit like asking how to write C code which can execute in a hardware interrupt handler. "What's an interrupt?", you might very well ask, "and what things that I might do in C aren't valid in one?". The only answers are OS-specific.

    0 讨论(0)
  • 2021-02-02 01:28

    It is a misconception that the pthreads library doesn't work on Windows. Check out sourceforge.net. I would recommend pthreads because it is cross-platform and its mutexes are way faster than e.g. the Windows builtin mutexes.

    0 讨论(0)
提交回复
热议问题