C# 访问列表<;T>;多线程中的元素并报告进度
在我的应用程序中,我有数据库中的人员列表。 对于每个人,我必须致电5(目前)服务来搜索一些信息。 如果服务返回信息,我会将其添加到该人员(特定人员的订单列表)C# 访问列表<;T>;多线程中的元素并报告进度,c#,multithreading,.net-3.5,C#,Multithreading,.net 3.5,在我的应用程序中,我有数据库中的人员列表。 对于每个人,我必须致电5(目前)服务来搜索一些信息。 如果服务返回信息,我会将其添加到该人员(特定人员的订单列表) 因为服务是独立工作的,所以我想我可以尝试并行运行它们。 我创建的代码如下所示: using System; using System.Collections.Generic; using System.Threading; namespace Testy { internal class Program {
因为服务是独立工作的,所以我想我可以尝试并行运行它们。 我创建的代码如下所示:
using System;
using System.Collections.Generic;
using System.Threading;
namespace Testy
{
internal class Program
{
internal class Person
{
public int Id { get; set; }
public string Name { get; set; }
public List<string> Orders { get; private set; }
public Person()
{
// thanks for tip @juharr
Orders = new List<string>();
}
public void AddOrder(string order)
{
lock (Orders) //access across threads
{
Orders.Add(order);
}
}
}
internal class Service
{
public int Id { get; private set; }
public Service(int id)
{
Id = id;
}
//I get error when I use IList instead of List
public void Search(ref List<Person> list)
{
foreach (Person p in list)
{
lock (p) //should I lock Person here? and like this???
{
Search(p);
}
}
}
private void Search(Person p)
{
Thread.Sleep(50);
p.AddOrder(string.Format("test order from {0,2}",
Thread.CurrentThread.ManagedThreadId));
Thread.Sleep(100);
}
}
private static void Main()
{
//here I load my services from external dll's
var services = new List<Service>();
for (int i = 1; i <= 5; i++)
{
services.Add(new Service(i));
}
//sample data load from db
var persons = new List<Person>();
for (int i = 1; i <= 10; i++)
{
persons.Add(
new Person {Id = i,
Name = string.Format("Test {0}", i)});
}
Console.WriteLine("Number of services: {0}", services.Count);
Console.WriteLine("Number of persons: {0}", persons.Count);
ManualResetEvent resetEvent = new ManualResetEvent(false);
int toProcess = services.Count;
foreach (Service service in services)
{
new Thread(() =>
{
service.Search(ref persons);
if (Interlocked.Decrement(ref toProcess) == 0)
resetEvent.Set();
}
).Start();
}
// Wait for workers.
resetEvent.WaitOne();
foreach (Person p in persons)
{
Console.WriteLine("{0,2} Person name: {1}",p.Id,p.Name);
if (null != p.Orders)
{
Console.WriteLine(" Orders:");
foreach (string order in p.Orders)
{
Console.WriteLine(" Order: {0}", order);
}
}
else
{
Console.WriteLine(" No orders!");
}
}
Console.ReadLine();
}
}
}
我已经添加了自定义EventArgs,服务类的事件。
在此配置中,我应该运行5个服务,但其中只有3个报告进度。我想象如果我有5项服务,我应该有5项活动(5行显示进度)。
这可能是因为线程,但我不知道如何解决这个问题 示例输出现在如下所示:
Number of services: 5
Number of persons: 10
Press ENTER to start...
Id: 3, Name: Service 3 - Progress: 10
Id: 4, Name: Service 4 - Progress: 10
Id: 5, Name: Service 5 - Progress: 19
END :)
Number of services: 5
Number of persons: 10
Press ENTER to start...
Id: 1, Name: Service 1 - Progress: 10
Id: 2, Name: Service 2 - Progress: 10
Id: 3, Name: Service 3 - Progress: 10
Id: 4, Name: Service 4 - Progress: 10
Id: 5, Name: Service 5 - Progress: 10
END :)
应该是这样的:
Number of services: 5
Number of persons: 10
Press ENTER to start...
Id: 3, Name: Service 3 - Progress: 10
Id: 4, Name: Service 4 - Progress: 10
Id: 5, Name: Service 5 - Progress: 19
END :)
Number of services: 5
Number of persons: 10
Press ENTER to start...
Id: 1, Name: Service 1 - Progress: 10
Id: 2, Name: Service 2 - Progress: 10
Id: 3, Name: Service 3 - Progress: 10
Id: 4, Name: Service 4 - Progress: 10
Id: 5, Name: Service 5 - Progress: 10
END :)
上次编辑
我已将所有线程创建移动到单独的类
ServiceManager
,现在我的代码看起来是这样的:
using System;
using System.Collections.Generic;
using System.Threading;
namespace Testy
{
internal class Program
{
public class ServiceEventArgs : EventArgs
{
public ServiceEventArgs(int sId, int progress)
{
SId = sId;
Progress = progress;
}
public int SId { get; private set; } // service id
public int Progress { get; private set; }
}
internal class Person
{
private static readonly object ordersLock = new object();
public int Id { get; set; }
public string Name { get; set; }
public List<string> Orders { get; private set; }
public Person()
{
Orders = new List<string>();
}
public void AddOrder(string order)
{
lock (ordersLock) //access across threads
{
Orders.Add(order);
}
}
}
internal class Service
{
public event EventHandler<ServiceEventArgs> ReportProgress;
public int Id { get; private set; }
public string Name { get; private set; }
public Service(int id, string name)
{
Id = id;
Name = name;
}
public void Search(List<Person> list)
{
int counter = 0;
foreach (Person p in list)
{
counter++;
Search(p);
var e = new ServiceEventArgs(Id, counter);
OnReportProgress(e);
}
}
private void Search(Person p)
{
p.AddOrder(string.Format("Order from {0,2}", Thread.CurrentThread.ManagedThreadId));
Thread.Sleep(50*Id);
}
protected virtual void OnReportProgress(ServiceEventArgs e)
{
var handler = ReportProgress;
if (handler != null)
{
handler(this, e);
}
}
}
internal static class ServiceManager
{
private static IList<Service> _services;
public static IList<Service> Services
{
get
{
if (null == _services)
Reload();
return _services;
}
}
public static void RunAll(List<Person> persons)
{
ManualResetEvent resetEvent = new ManualResetEvent(false);
int toProcess = _services.Count;
foreach (Service service in _services)
{
var local = service;
local.ReportProgress += ServiceReportProgress;
new Thread(() =>
{
local.Search(persons);
if (Interlocked.Decrement(ref toProcess) == 0)
resetEvent.Set();
}
).Start();
}
// Wait for workers.
resetEvent.WaitOne();
}
private static readonly object consoleLock = new object();
private static void ServiceReportProgress(object sender, ServiceEventArgs e)
{
lock (consoleLock)
{
Console.CursorTop = 1 + (e.SId - 1)*2;
int progress = (100*e.Progress)/100;
RenderConsoleProgress(progress, '■', ConsoleColor.Cyan, String.Format("{0} - {1,3}%", ((Service) sender).Name, progress));
}
}
private static void ConsoleMessage(string message)
{
Console.CursorLeft = 0;
int maxCharacterWidth = Console.WindowWidth - 1;
if (message.Length > maxCharacterWidth)
{
message = message.Substring(0, maxCharacterWidth - 3) + "...";
}
message = message + new string(' ', maxCharacterWidth - message.Length);
Console.Write(message);
}
private static void RenderConsoleProgress(int percentage, char progressBarCharacter,
ConsoleColor color, string message)
{
ConsoleColor originalColor = Console.ForegroundColor;
Console.ForegroundColor = color;
Console.CursorLeft = 0;
int width = Console.WindowWidth - 1;
var newWidth = (int) ((width*percentage)/100d);
string progBar = new string(progressBarCharacter, newWidth) + new string(' ', width - newWidth);
Console.Write(progBar);
if (!String.IsNullOrEmpty(message))
{
Console.CursorTop++;
ConsoleMessage(message);
Console.CursorTop--;
}
Console.ForegroundColor = originalColor;
}
private static void Reload()
{
if (null == _services)
_services = new List<Service>();
else
_services.Clear();
for (int i = 1; i <= 5; i++)
{
_services.Add(new Service(i, "Service " + i));
}
}
}
private static void Main()
{
var services = ServiceManager.Services;
int count = services.Count;
var persons = new List<Person>();
for (int i = 1; i <= 100; i++)
{
persons.Add(new Person {Id = i, Name = string.Format("Test {0}", i)});
}
Console.WriteLine("Services: {0}, Persons: {1}", services.Count, persons.Count);
Console.WriteLine("Press ENTER to start...");
Console.ReadLine();
Console.Clear();
Console.CursorVisible = false;
ServiceManager.RunAll(persons);
foreach (Person p in persons)
{
if (p.Orders.Count != count)
Console.WriteLine("{0,2} Person name: {1}, orders: {2}", p.Id, p.Name, p.Orders.Count);
}
Console.CursorTop = 12;
Console.CursorLeft = 0;
Console.WriteLine("END :)");
Console.CursorVisible = true;
Console.ReadLine();
}
}
}
protected virtual void OnReportProgress(ServiceEventArgs e)
{
EventHandler<ServiceEventArgs> handler = ReportProgress;
if (handler != null)
{
handler(this, e);
}
}
使用系统;
使用System.Collections.Generic;
使用系统线程;
名称空间测试
{
内部课程计划
{
公共类ServiceEventArgs:EventArgs
{
公共服务事件参数(int sId,int progress)
{
SId=SId;
进步=进步;
}
public int SId{get;private set;}//服务id
public int Progress{get;private set;}
}
内部阶级人士
{
私有静态只读对象ordersLock=new object();
公共int Id{get;set;}
公共字符串名称{get;set;}
公共列表顺序{get;private set;}
公众人士()
{
订单=新列表();
}
公共无效添加顺序(字符串顺序)
{
lock(ordersLock)//跨线程访问
{
订单。添加(订单);
}
}
}
内部班级服务
{
公共事件处理程序报告进度;
public int Id{get;private set;}
公共字符串名称{get;private set;}
公共服务(int-id,字符串名称)
{
Id=Id;
名称=名称;
}
公共无效搜索(列表)
{
int计数器=0;
foreach(列表中的人员p)
{
计数器++;
搜索(p);
var e=新的ServiceEventArgs(Id,计数器);
报告进展情况(e);
}
}
私人无效搜索(p人)
{
p、 AddOrder(string.Format(“来自{0,2}的顺序”,Thread.CurrentThread.ManagedThreadId));
睡眠(50*Id);
}
受保护的虚拟void OnReportProgress(ServiceEventArgs e)
{
var handler=ReportProgress;
if(处理程序!=null)
{
处理者(本,e);
}
}
}
内部静态类服务管理器
{
私人静态IList_服务;
公共静态IList服务
{
得到
{
如果(空==\u服务)
重新加载();
退货服务;
}
}
公共静态void RunAll(列出人员)
{
ManualResetEvent resetEvent=新的ManualResetEvent(错误);
int toProcess=\u services.Count;
foreach(服务中的服务)
{
var本地=服务;
local.ReportProgress+=服务报告进度;
新线程(()=>
{
本地搜索(人);
if(互锁减量(参考TopProcess)==0)
resetEvent.Set();
}
).Start();
}
//等待工人。
resetEvent.WaitOne();
}
私有静态只读对象控制台洛克=新对象();
私有静态void ServiceReportProgress(对象发送方,ServiceEventArgs e)
{
锁(控制台锁)
{
Console.CursorTop=1+(e.SId-1)*2;
内部进度=(100*e.progress)/100;
RenderConsoleProgress(进度,'■', ConsoleColor.Cyan,String.Format(“{0}-{1,3}%”,((服务)发送方.Name,progress));
}
}
专用静态void控制台消息(字符串消息)
{
Console.CursorLeft=0;
int maxCharacterWidth=Console.WindowWidth-1;
如果(message.Length>maxCharacterWidth)
{
message=message.Substring(0,maxCharacterWidth-3)+“…”;
}
消息=消息+新字符串(“”,maxCharacterWidth-message.Length);
控制台。写入(消息);
}
私有静态void RenderConsoleProgress(整数百分比、字符progressBarCharacter、,
控制台颜色,字符串消息)
{
ConsoleColor originalColor=Console.ForegroundColor;
Console.ForegroundColor=颜色;
Console.CursorLeft=0;
int width=Console.WindowWidth-1;
var newWidth=(int)((宽度*百分比)/100d);
string progBar=新字符串(progressBarCharacter,newWidth)+新字符串(“”,width-newWidth);
控制台写入(progBar);
如果(!String.IsNullOrEmpty(消息))
{
private object ordersLock = new object();
public void AddOrder(string order)
{
lock (ordersLock) //access across threads
{
Orders.Add(order);
}
}
foreach (Service service in services)
{
Service local = service;
local.ReportProgress += service_ReportProgress;
new Thread(() =>
{
local.Search(persons);
if (Interlocked.Decrement(ref toProcess) == 0)
resetEvent.Set();
}
).Start();
}
protected virtual void OnReportProgress(ServiceEventArgs e)
{
EventHandler<ServiceEventArgs> handler = ReportProgress;
if (handler != null)
{
handler(this, e);
}
}
private static object consoleLock = new object();
private static void service_ReportProgress(object sender, ServiceEventArgs e)
{
lock (consoleLock)
{
Console.CursorLeft = 0;
Console.CursorTop = e.SId;
Console.WriteLine("Id: {0}, Name: {1} - Progress: {2}", e.SId, ((Service)sender).Name, e.Progress);
}
}
...
Console.WriteLine("Number of services: {0}", services.Count);
Console.WriteLine("Number of persons: {0}", persons.Count);
Console.WriteLine("Press ENTER to start...");
Console.Clear();
Console.ReadLine();
...
Console.CursorTop = 6;
Console.WriteLine("END :)");