Since your lock only needs to protect the member variables you mutate, you can scope it to just that if/else block where you make your modifications. You do use _sequence
one additional time when generating the return value, but that could use a local copy acquired while the lock is held. So the main thing you are moving outside the lock
block is the call to GetTicks()
.
If you can figure out how to implement this with Interlocked functions, that's going to be the best performance. I'm not an expert, but I don't think you can do it directly, you would probably have to pack your sequence and generation into a single long. A ReaderWriterLockSlim offers more functionality than you need - you have no readers - so it's likely not optimal. You'll want to measure, but out of all the locks I'd guess you'd get the best performance from a SpinLock.
There's a whole use case you mention where these things are allocated per-thread, where presumably you don't want to pay for any thread safety overhead. I would suggest moving the code that requires locking into a separate virtual function with no thread-safety mechanisms, and making a subclass that overrides the implementation in a thread-safe way. This could be as simple as:
protected override long Increment()
{
lock (_genlock)
{
return base.Increment();
}
}