Bitterblue
Bitterblue

Reputation: 14075

C# - Where is Lock Appropriate to Use in Libraries?

I'm writing a library for multiple projects. And it contains classes that contain and manipulate data. So I thought I should lock this data while a method of those classes is executed. But I also thought if thread safe data manipulation must be done, then let the higher application layer handle locking. What would be the best practice for a library of classes to be used by actual applications later.

Let's say I have a class SpecialList. What do I do:

?

If I knew how the .NET Framework class List handles that I would do the same.


The classes are not explicitly targeted at single- or multi-threading. They are just helping classes for all kinds of uses.

Upvotes: 3

Views: 288

Answers (2)

Lasse V. Karlsen
Lasse V. Karlsen

Reputation: 391336

If you're designing data structures and types that needs to have a guarantee about thread safety, then sure, put locks and other constructs into those types to uphold those guarantees.

However, locks alone isn't enough.

Take a simple dictionary. Let's say you want to ensure the internal data structure inside the dictionary cannot be corrupted by multiple threads, so you introduce locks. However, if the outside code does this:

if (!dict.ContainsKey(key))
    dict.Add(key, value);

Then there is no guarantee here that between the call to ContainsKey and Add, some other thread hasn't already added that key to the dictionary.

As such, thread-safe types may entail more things than simple locks. A method that can safely add the key and value to the dictionary if it's not already there, in an atomic operation, and then return a flag telling you what it did, may be needed.

In fact, look here: ConcurrentDictionary.TryAdd.

My advice would be this:

  1. If you need those guarantees, design the type to be thread-safe by going through the scenarios that needs to be safe, and predictable, and implement those specifically
  2. If you don't need those guarantees, don't bother doing anything, instead simply document that the type is not thread-safe, leave that to the code that uses it

The .NET type List does not use locks in any way, except for a few select places where thread safety is a concern, specifically the SyncRoot property.

Additionally, writing good thread-safe data types isn't easy. Just throwing in locks everywhere you need them will probably make it more thread-safe, but you will pay a hefty penalty in terms of performance. If a program doesn't need it to be thread-safe when it uses it, you still pay a lot of that price.

Writing performant thread-safe types is harder, and does usually not rely on locks (alone), but instead uses things like spin-waits, specific CPU instructions (some of which are available to .NET code), etc. But this needs highly specialized knowledge about how modern CPU's execute code.

If I were you I would leave thread-safety to the experts, and drop it from your own types, unless you absolutely need it.

Upvotes: 5

BartoszKP
BartoszKP

Reputation: 35891

I think the answer is dependent on whether you want your library to be thread-safe or not. It seems trivial, but that's how it's done. Consider for example .NET's collections:

SynchronizedCollection

List (and other basic collections - not thread safe)

So a good choice seems to be doing both versions, or not thread-safe version only.

Upvotes: 1

Related Questions