我应该在这里使用 Interlocked.Exchange 还是标准写入就足够了?
Should I use Interlocked.Exchange here or is a standard write sufficient?
以下方法尝试获取与其他线程共享的锁。如果在一段时间内能够拿到锁,就会执行一些Action。
如果在指定的时间段内无法获得锁,我想通过生成一个Task在单独的线程上再次尝试整个过程。我不希望我的初始线程等待获得锁,因为在这种情况下性能很重要。但是,在此任务完成之前,我不希望再创建任何任务。换句话说,在任何给定时间都不应超过一个任务 运行。在任务仍为 运行 时对 'ExecuteOrOffload' 进行的任何后续调用不得创建另一个任务。
我想出了以下解决方案,我想知道我的 setter 方法 (setFlag) 是否应该使用 Interlocked.Exchange 来更改标志值,或者它是否有任何区别?代码运行在多核机器上
void Main()
{
//I currently use this
ExecuteOrOffload(_theLock, () => { }, () => _theFlag, newVal => _theFlag = newVal, 0);
//Should I use this instead? i.e. - Does my setter need to use Interlocked.Exchange?
ExecuteOrOffload(_theLock, () => { }, () => _theFlag, newVal => Interlocked.Exchange(ref _theFlag, newVal));
}
private object _theLock = new Object();
private int _theFlag = 0;
//Flag = 0. Allow a new Task to start if needed
//Flag = 1. A Task is already running. Don't allow new tasks to be created
private void ExecuteOrOffload(object thelock, Action theAction, Func<int> getFlag, Action<int> setFlag, int timeout = 0)
{
bool acquiredLock = false;
try
{
//If the lock can be obtained, execute the Action
Monitor.TryEnter(thelock, TimeSpan.FromSeconds(timeout), ref acquiredLock);
if (acquiredLock)
{
theAction();
}
//The lock was not obtained. Either offload to a new task or do nothing
else
{
//Get the current value of the flag
var currentValue = getFlag();
//Try set the flag to 1. Only one thread should manage to do this.
var originalValue = Interlocked.CompareExchange(ref currentValue, 1, 0);
//If this thread didn't change the flag then just return.
if (originalValue == currentValue)
{
return;
}
//The thread that gets here changes the actual flag value
setFlag(1);
//...and starts a new task
Task.Factory.StartNew(() =>
{
try
{
//Retry the whole process from a Task. This time the flag is set to 1 and a new Task will not be created should the lock time out
ExecuteOrOffload(thelock, theAction, getFlag, setFlag, 2);
}
finally
{
//Task completed (either timed out or executed the action, we don't care which). Reset the flag to allow future calls to start a new Task
setFlag(0);
}
});
}
}
catch (Exception e)
{
//Log exception
}
finally
{
//If this thread took the lock, release it
if (acquiredLock)
{
Monitor.Exit(thelock);
}
}
}
那个 get/setFlag 模式完全是活泼的。这不安全。您没有对共享变量进行 any 同步。
您正在做的 Interlocked.CompareExchange
是在一个非共享局部变量上。那无济于事。我们没有的智能 JIT 只会优化这种互锁操作(或将其转换为栅栏)。
对共享变量执行 Interlocked.CompareExchange
。这样就没有单独的商店了,你的问题就解决了。
以下方法尝试获取与其他线程共享的锁。如果在一段时间内能够拿到锁,就会执行一些Action。
如果在指定的时间段内无法获得锁,我想通过生成一个Task在单独的线程上再次尝试整个过程。我不希望我的初始线程等待获得锁,因为在这种情况下性能很重要。但是,在此任务完成之前,我不希望再创建任何任务。换句话说,在任何给定时间都不应超过一个任务 运行。在任务仍为 运行 时对 'ExecuteOrOffload' 进行的任何后续调用不得创建另一个任务。
我想出了以下解决方案,我想知道我的 setter 方法 (setFlag) 是否应该使用 Interlocked.Exchange 来更改标志值,或者它是否有任何区别?代码运行在多核机器上
void Main()
{
//I currently use this
ExecuteOrOffload(_theLock, () => { }, () => _theFlag, newVal => _theFlag = newVal, 0);
//Should I use this instead? i.e. - Does my setter need to use Interlocked.Exchange?
ExecuteOrOffload(_theLock, () => { }, () => _theFlag, newVal => Interlocked.Exchange(ref _theFlag, newVal));
}
private object _theLock = new Object();
private int _theFlag = 0;
//Flag = 0. Allow a new Task to start if needed
//Flag = 1. A Task is already running. Don't allow new tasks to be created
private void ExecuteOrOffload(object thelock, Action theAction, Func<int> getFlag, Action<int> setFlag, int timeout = 0)
{
bool acquiredLock = false;
try
{
//If the lock can be obtained, execute the Action
Monitor.TryEnter(thelock, TimeSpan.FromSeconds(timeout), ref acquiredLock);
if (acquiredLock)
{
theAction();
}
//The lock was not obtained. Either offload to a new task or do nothing
else
{
//Get the current value of the flag
var currentValue = getFlag();
//Try set the flag to 1. Only one thread should manage to do this.
var originalValue = Interlocked.CompareExchange(ref currentValue, 1, 0);
//If this thread didn't change the flag then just return.
if (originalValue == currentValue)
{
return;
}
//The thread that gets here changes the actual flag value
setFlag(1);
//...and starts a new task
Task.Factory.StartNew(() =>
{
try
{
//Retry the whole process from a Task. This time the flag is set to 1 and a new Task will not be created should the lock time out
ExecuteOrOffload(thelock, theAction, getFlag, setFlag, 2);
}
finally
{
//Task completed (either timed out or executed the action, we don't care which). Reset the flag to allow future calls to start a new Task
setFlag(0);
}
});
}
}
catch (Exception e)
{
//Log exception
}
finally
{
//If this thread took the lock, release it
if (acquiredLock)
{
Monitor.Exit(thelock);
}
}
}
那个 get/setFlag 模式完全是活泼的。这不安全。您没有对共享变量进行 any 同步。
您正在做的 Interlocked.CompareExchange
是在一个非共享局部变量上。那无济于事。我们没有的智能 JIT 只会优化这种互锁操作(或将其转换为栅栏)。
对共享变量执行 Interlocked.CompareExchange
。这样就没有单独的商店了,你的问题就解决了。