多线程这样访问List对吗?

游戏人间 2013-02-27 07:57:12
多线程这样访问List。

大家帮忙看一下DoWork这个函数这样访问List会不会有问题?



public partial class Form1 : Form
{
int count = 8;
object obj = new object();
Thread thread;
ManualResetEvent[] events;
List<TaskInfo> tasks;
public Form1()
{
InitializeComponent();
}

private void button1_Click(object sender, EventArgs e)
{
txtMsg.Text = "";
thread = new Thread(new ThreadStart(Test));
thread.Start();

}

public void Test()
{
events = new ManualResetEvent[count];
tasks = Init();
for (int i = 0; i < count; i++)
{
events[i] = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork), i);
}

ManualResetEvent.WaitAll(events);

InvokeSetText("任务完成");
}

public void DoWork(object o)
{
int i = (int)o;
TaskInfo model;
while (tasks.Count > 0)
{
Monitor.Enter(obj);
try
{
model = tasks[0];
tasks.RemoveAt(0);
}
finally
{
Monitor.Exit(obj);
}
Thread.Sleep(2000);
//长任务抓网页Snap(model.Url);
InvokeSetText(string.Format("{0} 线程:{1}",model.Name,o));
Thread.Sleep(1000);
}
events[i].Set();
}

public delegate void SetTextHandler(string msg);

public void InvokeSetText(string msg)
{
SetTextHandler d = new SetTextHandler(SetText);
Invoke(d, msg);
}

public void SetText(string msg)
{
txtMsg.Text = msg + "\r\n" + txtMsg.Text;
}


public List<TaskInfo> Init()
{
List<TaskInfo> taskInfos = new List<TaskInfo>();
TaskInfo task;
for (int i = 0; i < 30; i++)
{
task = new TaskInfo(i.ToString(), string.Format("任务{0}", i),"www.hao123.com");
taskInfos.Add(task);
}

return taskInfos;

}

private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (thread != null)
{
thread.Abort();

}
}
}

public class TaskInfo
{
private string m_Id;
private string m_Name;
private string m_Url;

public string Id
{
get { return m_Id; }
set { m_Id = value; }
}


public string Name
{
get { return m_Name; }
set { m_Name = value; }
}

public string Url
{
get { return m_Url; }
set { m_Url = value; }
}

public TaskInfo()
{ }

public TaskInfo(string _id, string _name,string _url)
{
m_Id = _id;
m_Name = _name;
m_Url = _url;
}

}
...全文
454 20 打赏 收藏 转发到动态 举报
写回复
用AI写文章
20 条回复
切换为时间正序
请发表友善的回复…
发表回复
梨花树下思考 2013-03-21
  • 打赏
  • 举报
回复
引用 7 楼 lye2000000_super 的回复:
做了lock就没问题的。
游戏人间 2013-03-21
  • 打赏
  • 举报
回复
贴子又沉了。
  • 打赏
  • 举报
回复
引用 11 楼 wonderfuly 的回复:
这个例子就是用来测试的,但是测试了很久都没发现什么问题 。 就是担心会不会出现,像 @qldsrx 同志说的问题??
测试了还担心,这就是不应该的!!! 可能你没有习惯于大规模测试。你可以写3、5个测试过程,然后并行(比如说20个线程同时)执行10遍,如果没有出现问题基本上就可以有勇气说:我现在没有问题。 别人说什么都不算数,别人只能给你一个参考启发。你把任何疑问都写成测试程序,直到实在写不出来了,这时候就不必再担心别人说什么了!
  • 打赏
  • 举报
回复
你这里弄一个 ManualResetEvent[] events; 显得非常画蛇添足。 多线程是一回事,而异步是另一回事,异步操作是高级的多线程操作,是没有“阻塞”的多线程操作。 你完全可以不需要这个events,也就是不阻塞Test方法,让它直接结束。然后当最后一个任务结束,也就是 tasks.Count 为 0 的时候,在那个线程上执行Test原本打算在所有任务结束时进行的操作就行了! 异步操作尽量不要阻塞,尽量不要使用 ManualResetEvent。实际上只有我们“被迫”(比如说被垃圾需求分析所强迫)不得不用多线程方法来模拟顺序操作时,往往才阻塞自己。
  • 打赏
  • 举报
回复
引用 11 楼 wonderfuly 的回复:
这个例子就是用来测试的,但是测试了很久都没发现什么问题 。 就是担心会不会出现,像 @qldsrx 同志说的问题??
那么你可以在Monitor.Enter之后再次判断,例如
lock(obj)
{   
   if(task.count==0)
      break;
   else
   {
      model = tasks[0];      
      tasks.RemoveAt(0); 
   }
 }
游戏人间 2013-03-12
  • 打赏
  • 举报
回复
这个例子就是用来测试的,但是测试了很久都没发现什么问题 。 就是担心会不会出现,像 @qldsrx 同志说的问题??
  • 打赏
  • 举报
回复
你这种场景用Queue再好不过了 while(true) { 先去送; 看一下Queue里面还没有待送的? 这里和添加新任务的时候做一下同步就可以了 有就取出,并进入下一轮,没有就break } 主线程中要做的是,如果当前有任务,并且还有人空闲着(简单计数就可以),那就指派一个人
游戏人间 2013-03-12
  • 打赏
  • 举报
回复
举个比较简单的例子。 一个快递公司今天有8份快递(8个任务),7个业务员去送快递(7个线程),有2个业务员比较快完成任务,回到公司领取任,前一个业务员领取到第8个任务,另一个人去领进候任务已经没有了。tasks.Count==0 像 sp1234 同志说的这个人报说任务完成了,显示是不正确的。因为其人是否已完成快递任还不知道,必须等他们都回来报告才知道,任务是否都完成也就是 ManualResetEvent[] events 全部 Set.
游戏人间 2013-03-12
  • 打赏
  • 举报
回复
引用 13 楼 sp1234 的回复:
你这里弄一个 ManualResetEvent[] events; 显得非常画蛇添足。 多线程是一回事,而异步是另一回事,异步操作是高级的多线程操作,是没有“阻塞”的多线程操作。 你完全可以不需要这个events,也就是不阻塞Test方法,让它直接结束。然后当最后一个任务结束,也就是 tasks.Count 为 0 的时候,在那个线程上执行Test原本打算在所……
对于sp1234同志说的这个 “然后当最后一个任务结束,也就是 tasks.Count 为 0 的时候...." 我认为是不正确的,当一个线程执行到tasks.Count为0 时,这个线程报告任务结束,是不能进行任务完成判断的,因为其它线程可能还在执行先前取到的task. 只能说明tasks没有任务了。而不能说明任务已完成。
autoid1 2013-03-08
  • 打赏
  • 举报
回复
把try去掉. 自己加数据多测试几次就知道有没有问题了.
qldsrx 2013-03-08
  • 打赏
  • 举报
回复
while (tasks.Count > 0) { Monitor.Enter(obj); try { model = tasks[0]; tasks.RemoveAt(0); } finally { Monitor.Exit(obj); } Thread.Sleep(2000); //长任务抓网页Snap(model.Url); InvokeSetText(string.Format("{0} 线程:{1}",model.Name,o)); Thread.Sleep(1000); } 第一行判断了tasks.Count > 0,然后Monitor.Enter(obj);进行同步对象,一个时间只允许一个线程访问它,对List操作,但是问题还是会有,你后面执行model = tasks[0];的时候,没有再次判断tasks.Count,因为即使你锁定obj,但是你while (tasks.Count > 0)的执行是在Monitor.Enter(obj);前面,因此必定会执行前面那行操作,只是在后面的Monitor.Enter(obj);才进行同步,这样就可能会发生前面判断的时候tasks.Count > 0成立,后面等待了会后那个tasks.Count改变了,变成了0.
游戏人间 2013-03-08
  • 打赏
  • 举报
回复
贴子又沉了。
  • 打赏
  • 举报
回复
做了lock就没问题的。
游戏人间 2013-02-28
  • 打赏
  • 举报
回复
现在不讨论用Queue<T>还是List<T>,只求程序能在多线程下安全运行。 呵呵! 以后我会试着用Queue<T>的。
threenewbee 2013-02-28
  • 打赏
  • 举报
回复
引用 3 楼 wonderfuly 的回复:
wddw1986 Queue也考虑过,小项目就用 List不。
有什么差别呢?只是你没有用过Queue<T>,人为地认为它“很难”。
stonespace 2013-02-28
  • 打赏
  • 举报
回复
其实在这个问题中,Queue不论是操作简单上还是内存占用上,都比List好,
引用 3 楼 wonderfuly 的回复:
wddw1986 Queue也考虑过,小项目就用 List不。
游戏人间 2013-02-27
  • 打赏
  • 举报
回复
wddw1986 Queue也考虑过,小项目就用 List不。
cheng2005 2013-02-27
  • 打赏
  • 举报
回复
先不说有没有问题, 在你当前的需求下 List<TaskInfo> tasks; 这个结构不如先改成 Queue<TaskInfo> tasks; 这样有利于你以后扩展
gxingmin 2013-02-27
  • 打赏
  • 举报
回复
貌似可以,简单点写法也可以这样 lock(obj) { model = tasks[0]; tasks.RemoveAt(0); }

110,570

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧