鉴于可以并行运行的操作列表,我想考虑一个或另一个操作不希望与其他操作并行执行(由属性确定)的行动)。
考虑这种简化:
private static Random random;
static void Main (string[] args)
{
random = new Random();
new Thread (() => DoSomething (false)).Start();
new Thread (() => DoSomething (false)).Start();
new Thread (() => DoSomething (true)).Start();
new Thread (() => DoSomething (false)).Start();
new Thread (() => DoSomething (false)).Start();
Console.Read();
}
private static void DoSomething(bool singlethread)
{
Console.WriteLine ("Entering " + Thread.CurrentThread.ManagedThreadId);
if (singlethread)
Console.WriteLine ("Im the only one!!!");
Thread.Sleep (random.Next (1000, 5000));
Console.WriteLine ("Exiting " + Thread.CurrentThread.ManagedThreadId);
}
如何同步动作,以便动作3等待动作1& 2退出然后阻止动作4和5?
这是我在Rob的帮助下得出的结论:
public class Lock : IDisposable
{
private static readonly object _object = new object();
private static readonly AutoResetEvent _event = new AutoResetEvent (false);
private static int _count;
public static IDisposable Get (bool exclusive)
{
return new Lock (exclusive);
}
private readonly bool _wasTaken;
private Lock (bool exclusive)
{
if (exclusive)
{
Monitor.Enter (_object, ref _wasTaken);
_count++;
while (_count > 1)
_event.WaitOne();
}
else
{
lock (_object)
Interlocked.Increment (ref _count);
}
}
public void Dispose ()
{
Interlocked.Decrement (ref _count);
if (_wasTaken)
Monitor.Exit (_object);
_event.Set();
}
}
使用方法如下:
using (Lock.Get(exclusive: false/true)
{
DoSomething();
}
答案 0 :(得分:4)
您可以在名为ConcurrentExclusiveSchedulerPair的特殊调度程序上启动Thread
,而不是Task
。它允许您将任务声明为并发或独占。
它的作用与ReaderWriterLockSlim
的作用相同,并且让并发就绪的工作项采用读锁定,同时使独占项采用写锁定。
如果我理解你的情况,这应该满足你的需要,并且不再需要在这个帖子中发布的所有手动同步代码。
答案 1 :(得分:2)
这是一个非常常见的心理障碍,解决方案是微不足道的。您的要求是按顺序执行方法A,B和C.因此,让我们假设您启动一个执行A的线程,然后将所有锁定放在适当的位置以确保另一个线程在A完成之前不执行B.
您所忽略的是A完成后执行A的线程将要执行的操作。从你的臀部开始,你可能会说你允许它终止,该线程的工作已经完成。你启动它来执行A,你只需要它。您的锁定设计将确保另一个线程在正确的时间执行B.
但是,等等,你已经有一个非常好的候选人来执行B.执行A的线程。你没有用它来做任何事情。所以只需将该工作交给同一个线程。巨大的优势,你根本不需要任何锁定来确保这个线程在正确的时间执行B:
void DoWork(object state) {
A();
B();
C();
ItIsReallyDone.Set();
}
轻松,轻松。
答案 2 :(得分:2)
根据usr的建议,这是另一种无需手动同步即可实现的实现。
class Program
{
private static readonly Random Random = new Random();
private static readonly ConcurrentExclusiveSchedulerPair TaskSchedulerPair = new ConcurrentExclusiveSchedulerPair();
static void Main()
{
DoSomething(false);
DoSomething(false);
DoSomething(true);
DoSomething(false);
DoSomething(false);
Console.Read();
}
private static void DoSomething(bool singleThread)
{
var scheduler = singleThread ? TaskSchedulerPair.ExclusiveScheduler : TaskSchedulerPair.ConcurrentScheduler;
Task.Factory.StartNew(() =>
{
if (singleThread)
Console.WriteLine("Starting exclusive task.");
DoSomething();
}, new CancellationToken(), TaskCreationOptions.LongRunning, scheduler)
.ContinueWith(_ =>
{
if (singleThread)
Console.WriteLine("Finished exclusive task.");
});
}
private static void DoSomething()
{
Console.WriteLine("Starting task on thread {0}.", Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(Random.Next(1000, 5000));
Console.WriteLine("Finished task on thread {0}.", Thread.CurrentThread.ManagedThreadId);
}
}
答案 3 :(得分:1)
有趣的问题。您可以使用引用计数。
我已经在控制台上测试了几次,看起来它做得对了。
class Program
{
private static Random random;
private static int refCount;
private static object syncObject = new object();
private static AutoResetEvent resetEvent = new AutoResetEvent(true);
static void Main(string[] args)
{
random = new Random();
new Thread(() => DoSomething(false)).Start();
new Thread(() => DoSomething(false)).Start();
new Thread(() => DoSomething(true)).Start();
new Thread(() => DoSomething(false)).Start();
new Thread(() => DoSomething(false)).Start();
Console.Read();
}
private static void DoSomething(bool singleThread)
{
if (singleThread)
{
lock (syncObject)
{
++refCount;
// Inside this lock, no new thread can get to the parameterless DoSomething() method.
// However, existing threads can continue to do their work, and notify this thread that they have finished by signalling using the resetEvent.
while (refCount > 1)
resetEvent.WaitOne();
Console.WriteLine("Starting exclusive task.");
DoSomething();
Console.WriteLine("Finished exclusive task.");
}
}
else
{
lock (syncObject)
++refCount;
DoSomething();
}
--refCount;
resetEvent.Set();
}
private static void DoSomething()
{
Console.WriteLine("Starting task on thread {0}", Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(1000);
Console.WriteLine("Finished task on thread {0}", Thread.CurrentThread.ManagedThreadId);
}
}