user2050247
user2050247

Reputation: 23

Locking in IHostedService

I have IHostedService inheritor service, with process some data, and store the last part in a variable.

public class MyService : IHostedService 
{
    private DataType lastData;
    public DataType GetLastData()
    {
       return lastData;
    }

    public void ProcessNextPart()
    {
        ...
    } 
}

I also have api controller, which use DI to call MyService. Should i use lock or some other approach for lastData in this case? As I understand, simultaneous reading (from controller) and writing (from service) for that variable is possible?

Thanks.

Upvotes: 0

Views: 880

Answers (1)

Kevin Gosse
Kevin Gosse

Reputation: 39027

It all depends on whether DataType is a class or a struct.

If it's a class, then writing it is atomic, so it's pretty much safe. If you think the caller could use that value in a loop, then you may want to add a volatile read to prevent the value from being cached in a register:

public class MyService : IHostedService 
{
    private DataType lastData;

    public DataType GetLastData()
    {
       return Volatile.Read(ref lastData);
    }

    public void ProcessNextPart()
    {
        lastData = newValue;
    } 
}

But given the use-case, that sounds highly unlikely.

If DataType is a struct, then it's a whole different story. If the struct is bigger than the target architecture pointer size (4 bytes for x86, 8 bytes for x64), then writing it isn't atomic. Even if the struct is small enough, I don't recommend relying on it since you could add more fields later and break this code. So you have two solutions: either use a lock, or box the value.

Using a lock:

public class MyService : IHostedService 
{
    private readonly object syncRoot = new object();
    private DataType lastData;

    public DataType GetLastData()
    {
       lock (syncRoot)
           return lastData;
    }

    public void ProcessNextPart()
    {
        lock (syncRoot)
            lastData = newValue;
    } 
}

Boxing the value:

using System.Runtime.CompilerServices;

public class MyService : IHostedService 
{
    private readonly object syncRoot = new object();
    private StrongBox<DataType> lastData;

    public DataType GetLastData()
    {
        return lastData.Value;
    }

    public void ProcessNextPart()
    {
        lastData = new StrongBox<DataType>(newValue);
    } 
}

Upvotes: 1

Related Questions