枚举器中的多线程

时间:2018-02-02 08:24:40

标签: c# multithreading locking

我正在使用C#,我有一个枚举器,我按顺序读取枚举器中的数据。

这是第三方库对象,不支持Parallel.Foreach

while(enumerator.Next())
{
  var item = enumerator.Read();
  ProcessItem(item);
}

ProcessItem(Item item)
{
// Is lock required here
 if(item.prop == "somevalue")
   this._list.Add(item);
}

我想在阅读内容时在这里实现多线程。

while(enumerator.Next())
{
  // This code should run in a multi-threaded way

  var item = enumerator.Read();
  // ProcessItem method puts these items on a class level list property 
  // Is there any Lock required?
  ProcessItem(item);
}

我是多线程的新手。请分享满足上述要求的任何代码样本。

2 个答案:

答案 0 :(得分:2)

是的,需要一些锁定。你可以使用锁或使用并发集合类型来实现它。

使用lock

ProcessItem(Item item)
{
    if(item.prop == "somevalue")
    {
        lock(_list)
        {
            _list.Add(item);
        }
    }
}

编辑:根据您提供的详细信息,您可以使用自己的枚举器从外部lib中包装enumerator,以便您可以使用Parallel.ForEach

我们假设您获得的枚举器类似于MockEnumerator,我们将其包装在普通的IEnumerator中,并且IEnumerable使我们能够使用Parallel.ForEach并行读取。

class Program
{
    class Item
    {
        public int SomeProperty { get; }

        public Item(int prop)
        {
            SomeProperty = prop;
        }
    }

    class MockEnumerator
    {
        private Item[] _items = new Item[] { new Item(1), new Item(2) };
        private int _position = 0;

        public bool Next()
        {
            return _position++ < _items.Length;
        }

        public Item Read()
        {
            return _items[_position];
        }
    }

    class EnumeratorWrapper : IEnumerator<Item>, IEnumerable<Item>
    {
        private readonly MockEnumerator _enumerator;

        public EnumeratorWrapper(MockEnumerator enumerator)
        {
            this._enumerator = enumerator;
        }

        public Item Current => _enumerator.Read();

        object IEnumerator.Current => Current;

        public void Dispose()
        {
        }

        public IEnumerator<Item> GetEnumerator()
        {
            throw new NotImplementedException();
        }

        public bool MoveNext()
        {
            return _enumerator.Next();
        }

        public void Reset()
        {
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return this;
        }
    }

    private static List<Item> _list = new List<Item>();

    static void Main(string[] args)
    {
        var enumerator = new EnumeratorWrapper(new MockEnumerator());
        Parallel.ForEach(enumerator, item =>
        {
            if (item.SomeProperty == 1)//someval
            {
                lock (_list)
                {
                    _list.Add(item);
                }
            }
        });
    }
}

答案 1 :(得分:1)

这是基于任务的并行化的一个很好的例子。项目的每个处理对应于任务。因此,您可以将循环更改为以下内容:

  var tasks = new List<Task<int>>();
  while(enumerator.MoveNext())
  {
    var item = enumerator.Current;

    Task<int> task = new Task<int>(() => ProcessItem(item));
    task.Start();
    tasks.Add(task);
  }


  foreach(Task<int> task in tasks)
  {
    int i = task.Result;
    classList.Add(i);
  }

请注意,classList上的同步是通过首先在while循环中生成所有任务,然后在foreach循环中合并结果来隐式给出的。通过Result访问 string[] libraryCssList = new string[] { "~/Content/css1.css", "~/Content/css2.css", }; // works fine // error begins: bundles.UseCdn = true; var fontRokkittCss = "https://fonts.googleapis.com/css?family=Rokkitt:200,300,400,700"; bundles.Add(new StyleBundle("~/Content/libraryCss") .Include(libraryCss) .Include(fontRokkittCss)); 来明确同步。