use*_*096 0 c# parallel-processing multithreading thread-safety threadpool
我正在尝试动态锁定线程,但无论我尝试什么,总会发生某种竞争条件,这似乎是由多个线程同时启动任务引起的,但我一直无法找到一个好的答案这。
这是一个例子:
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
public class Example
{
public static readonly Object padLock = new Object();
public readonly static ConcurrentDictionary<string, object> locks = new
ConcurrentDictionary<string, object>();
public static List<string> processes = new List<string>();
public static void Main()
{
//Add random list of processes (just testing with one for now)
for (var i = 0; i < 1; i++) {
processes.Add("random" + i.ToString());
}
while (true)
{
foreach (var process in processes )
{
var currentProc = process ;
lock (padLock)
{
if (!locks.ContainsKey(currentProc))
{
System.Threading.Tasks.Task.Factory.StartNew(() =>
{
if (!locks.ContainsKey(currentProc))
{
var lockObject = locks.GetOrAdd(currentProc, new object());
lock (lockObject)
{
Console.WriteLine("Currently Executing " + currentProc);
Console.WriteLine("Ended Executing " + currentProc);
((IDictionary)locks).Remove(currentProc);
}
}
});
}
}
// Thread.Sleep(0);
}
}
Console.ReadLine();
}
}
Run Code Online (Sandbox Code Playgroud)
输出:
Started 1
Finished 1
Started 1
Finished 1
Started 1
Finished 1
Run Code Online (Sandbox Code Playgroud)
但有时会得到:
Started 1
Started 1
Finished 1
Run Code Online (Sandbox Code Playgroud)
这是不希望的,动态锁应该锁定它并只执行一次
我将您的要求理解为“获取进程列表,然后使用多个线程对每个进程仅执行一次”。
出于示例的目的,假设Foo(process)
工作单元只能完成一次。
这是一个非常常见的需求,有多种模式。
该技术可以为循环的每次迭代使用不同的线程,该线程将同时执行。
Parallel.ForEach(processes, process => Foo(process));
Run Code Online (Sandbox Code Playgroud)
是的; 这是一行代码。
如果是异步的,则此技术是合适的Foo()
。它只是为所有进程安排一个任务,然后等待它们,并让 SynchronizationContext 对其进行排序。
var tasks = processes.Select( p => Foo(process) );
await Task.WhenAll(tasks);
Run Code Online (Sandbox Code Playgroud)
这使用了生产者-消费者模式,这是一个线程添加到队列而另一个线程从队列中获取的传统方式。通过从队列中删除一个项目,它被有效地“锁定”,以便其他线程不会尝试对其进行操作。
BlockingCollection<string>() queue = new BlockingCollection<string>();
void SetUpQueue()
{
for (int i=0; i<100; i++) queue.Add(i.ToString());
queue.CompleteAdding();
}
void Worker()
{
while (queue.Count > 0 || !queue.IsAddingCompleted)
{
var item = queue.Take();
Foo(item);
}
}
Run Code Online (Sandbox Code Playgroud)