Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何动态锁定线程并避免争用情况_C#_Multithreading_Parallel Processing_Thread Safety_Threadpool - Fatal编程技术网

C# 如何动态锁定线程并避免争用情况

C# 如何动态锁定线程并避免争用情况,c#,multithreading,parallel-processing,thread-safety,threadpool,C#,Multithreading,Parallel Processing,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

我正在尝试动态锁定线程,但无论我尝试什么,总是会出现某种争用情况,这似乎是由多个线程同时启动任务造成的,但我一直未能找到一个很好的答案

以下是一个例子:

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();
 }
}
但有时:

Started 1

Started 1

Finished 1

这是不需要的,动态锁应该锁定它并只执行一次

虽然我不知道您正在尝试做什么,但您看到的行为是因为您正在从多个线程中滥发Console.WriteLine,它们被无序打印。我通过将时间戳附加到Console.WriteLine来验证:

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>(); 

  [ThreadStatic]
  private static bool flag = false;

 public static void Main()
 {
  //Add random list of processes (just testing with one for now)
  for (var i = 0; i < 10; i++)
  {
     processes.Add(i.ToString());
  }

  while (true)
  {
     foreach (var process in processes)
     {
        var currentProc = process; 

        if (!locks.ContainsKey(currentProc))
        {
           var lockObject = locks.GetOrAdd(currentProc, new object());
           Task.Factory.StartNew(() =>
            {
               lock (lockObject)
               {
                  if (flag) throw new Exception();
                  flag = true;
                  Console.WriteLine("Currently Executing " + currentProc);
                  Thread.Sleep(0); // You can siimulate work here
                  Console.WriteLine("Ended Executing " + currentProc);
                  flag = false;
                  ((IDictionary)locks).Remove(currentProc);
               }
            });
        }
     }
   }
  }
}

虽然我不知道你在尝试做什么,但你看到的行为是因为你从多个线程中滥发Console.WriteLine,它们被打印出了错误。我通过将时间戳附加到Console.WriteLine来验证:

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>(); 

  [ThreadStatic]
  private static bool flag = false;

 public static void Main()
 {
  //Add random list of processes (just testing with one for now)
  for (var i = 0; i < 10; i++)
  {
     processes.Add(i.ToString());
  }

  while (true)
  {
     foreach (var process in processes)
     {
        var currentProc = process; 

        if (!locks.ContainsKey(currentProc))
        {
           var lockObject = locks.GetOrAdd(currentProc, new object());
           Task.Factory.StartNew(() =>
            {
               lock (lockObject)
               {
                  if (flag) throw new Exception();
                  flag = true;
                  Console.WriteLine("Currently Executing " + currentProc);
                  Thread.Sleep(0); // You can siimulate work here
                  Console.WriteLine("Ended Executing " + currentProc);
                  flag = false;
                  ((IDictionary)locks).Remove(currentProc);
               }
            });
        }
     }
   }
  }
}
这是一个常见的问题 我将您的需求理解为“获取一个进程列表,然后使用多个线程对每个进程只做一次”

在我的示例中,假设
Foo(process)
只完成一次工作单元

这是一个非常常见的需求,有几种模式

并行循环 这种技术可以为循环的每次迭代使用不同的线程,循环将并发执行

Parallel.ForEach(processes, process => Foo(process));
对,;这是一行代码

异步任务 如果
Foo()
是异步的,则此技术适用。它只是为所有进程安排一个任务,然后等待它们,并让SynchronizationContext进行排序

var tasks = processes.Select( p => Foo(process) );
await Task.WhenAll(tasks);
生产者/消费者 这使用了,这是一个线程添加到队列,另一个线程从队列中获取的传统方式。通过从队列中删除一个项目,它将被有效地“锁定”,以便其他线程不会尝试处理它

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);
    }
}
BlockingCollection()队列=新建BlockingCollection();
void SetUpQueue()
{
for(int i=0;i 0 | |!queue.isadding已完成)
{
var item=queue.Take();
Foo(项目),;
}
}
这是一个常见问题 我将您的需求理解为“获取一个进程列表,然后使用多个线程对每个进程只做一次”

在我的示例中,假设
Foo(process)
只完成一次工作单元

这是一个非常常见的需求,有几种模式

并行循环 这种技术可以为循环的每次迭代使用不同的线程,循环将并发执行

Parallel.ForEach(processes, process => Foo(process));
对,;这是一行代码

异步任务 如果
Foo()
是异步的,则此技术适用。它只是为所有进程安排一个任务,然后等待它们,并让SynchronizationContext进行排序

var tasks = processes.Select( p => Foo(process) );
await Task.WhenAll(tasks);
生产者/消费者 这使用了,这是一个线程添加到队列,另一个线程从队列中获取的传统方式。通过从队列中删除一个项目,它将被有效地“锁定”,以便其他线程不会尝试处理它

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);
    }
}
BlockingCollection()队列=新建BlockingCollection();
void SetUpQueue()
{
for(int i=0;i 0 | |!queue.isadding已完成)
{
var item=queue.Take();
Foo(项目),;
}
}


hrmm
Started
在您的代码中没有表示,只是说您想实现什么?如果您只想确保每个对象只处理一次,那么这是一种非常不寻常的模式。@常规当前已启动executing@JohnWu你会怎么做?我们不知道你想做什么。这样做的目的是什么?hrmm
Started
在您的代码中没有表示,只是说您想实现什么?如果您只想确保每个对象只处理一次,那么这是一种非常不寻常的模式。@常规当前已启动executing@JohnWu你会怎么做?我们不知道你想做什么。这样做的目的是什么?这是一个有用的答案,但这并不准确,我附上了你所说的记号,是的,它们是无序的,但如果你复制并粘贴,你会注意到仍然有发生。我在上面的代码中添加了一个标志,如果这两个事件出现无序,将抛出异常,没有抛出异常。我在本地运行了你的代码,它在中间位置@Miner抛出了一个异常。这很有趣,我想知道为什么@Miner这不起作用。我将用我实际运行的代码进行编辑,它工作正常。这是一个有用的答案,但这不准确,我附加了记号,正如你所说的,是的,它们是无序的,但如果你复制并粘贴,你会注意到仍然有发生。我在上面的代码中添加了一个标志,如果这两个事件出现无序,将引发异常,并且不会引发异常。我在本地运行了你的代码,它在mediately@miner中引发了异常。不过这很有趣,我想知道为什么@Miner不起作用,我要用我实际运行的代码进行编辑,并且并行运行得很好。ForEach似乎给出了正确的结果,我只是不明白为什么即使在while循环中也能起作用,你能详细说明一下这一部分吗?对不起,我不明白你的问题。你在哪个示例中遇到了问题?我的意思是,Parallel.ForEach似乎可以工作,但我不明白的是,为什么它不启动同一个线程两次,即使它在while循环中。这个循环非常复杂,但它似乎将列表存储为数组,创建了一系列“worker”(取决于最大并行度和CPU中的内核数),然后每个工作人员从数组中抓取项目并对其进行处理。如果列表中的元素太多,实际上可以重复使用线程,但这是意料之中的。感谢@JohnWu,非常感谢您的输入,但我觉得MineR更倾向于我要完成的任务,