http://www.sufeinet.com/plugin.php?id=keke_group

苏飞论坛

 找回密码
 马上注册

QQ登录

只需一步,快速开始

分布式系统框架(V2.0) 轻松承载百亿数据,千万流量!讨论专区 - 源码下载 - 官方教程

HttpHelper爬虫框架(V2.7-含.netcore) HttpHelper官方出品,爬虫框架讨论区 - 源码下载 - 在线测试和代码生成

HttpHelper爬虫类(V2.0) 开源的爬虫类,支持多种模式和属性 源码 - 代码生成器 - 讨论区 - 教程- 例子

查看: 21698|回复: 12

[其他] 如何解决多线程同时访问一个资源的问题?

[复制链接]
发表于 2014-4-16 13:36:12 | 显示全部楼层 |阅读模式
本帖最后由 sandy1231 于 2014-4-17 10:07 编辑

我有一个数组,里面大约有1000条数据要Post出去,想把这一千条数据分成10个线程,每个线程POST 100条。但是执行的时候有的线程重复读取这个数组了,所以造成很多数据都重复发送的问题,如果用lock语句的话,感觉和单线程没啥区别,请问怎么解决?

尝试了把1000条数据分成10个集合数组,但发现运行起来后  集合会报 “未将对象引用设置到对象的实例。”或者是 “超出数组界限”之类的错误。
[C#] 纯文本查看 复制代码
 static void Main(string[] args)
        {
            Thread[] t = new Thread[10];
            ArrayList list = new ArrayList();
            ArrayList[] temp = new ArrayList[10];
            
            for (int i = 0; i < 1000; i++)
            {
                list.Add(i.ToString());
            }
            for (int i = 0; i < 10; i++)
            {
                temp[i][i] = [/i]new ArrayList();
 for (int j = 0; j < 100; j++)
                {
                    temp.Add(list[j]);
                } 
                list.RemoveRange(0, 99);
                t = new Thread(new ThreadStart(delegate
                    {
                        for (int k = 0; k < temp.Count; k++)
                        {
                            TestFor(temp[i][k].ToString());
                        } 
                    }
                    ));
                t.Name = i.ToString();
                t.Start();
                Thread.Sleep(1000);
}
            }

 public static void TestFor(string value)
        {
            Console.WriteLine("这是线程"+Thread.CurrentThread.Name+":"+value);
            Thread.Sleep(10);
        }




这里的代码会报 “未将对象引用设置到对象的实例。”或者是 “超出数组界限”之类的错误。 指向的错误是集合数组temp。
估计原因是:每循环开启一个线程后,  然后给数组赋值了,而正在运行的线程里的数组也会变成数组,因此被新值覆盖,所以出现了错误。


请问原因是这样么?


但我感觉如果是这样的话 有点不符合逻辑,线程1 调用的是 数组1,线程2 调用的是 数组2 ,循环一次后数组2的值变了 ,但为什么原先线程1里的数组1 也变数组2了?他们各自的值不是放到各自的堆栈里的吗?

后来我发现如果把方法TestFor里面做成循环,线程调用的时候直接传一个数组过去作为参数,而不是在线程里循环执行方法,这样上面的代码就不报错了,奇怪,有大神讲解下么?

[C#] 纯文本查看 复制代码
static void Main(string[] args)
    {
        Thread[] t = new Thread[10];
        ArrayList list = new ArrayList();
        ArrayList[] temp = new ArrayList[10];
         
     for (int i = 0; i < 1000; i++)
        {
            list.Add(i.ToString());
        }
        for (int i = 0; i <t.Length; i++)
        {
            temp[i] = new ArrayList();
            for (int j = 0; j < 100; j++)
            {
                temp.Add(list[j]);
            } 
            list.RemoveRange(0, 99);
            t = new Thread(new ThreadStart(delegate
                {
                        TestFor(temp[i]);
                }
                ));
            t.Name = i.ToString();
            t.Start();
            Thread.Sleep(200);
        }
        Console.ReadKey();
        
    }
 
    public static void TestFor(ArrayList list)
    {
        for (int i = 0; i < 100; i++)
        {
            Console.WriteLine("这是线程" + Thread.CurrentThread.Name + ":" + list.ToString());
            Thread.Sleep(10);
        }  
    }


1. 开通SVIP会员,免费下载本站所有源码,不限次数据,不限时间
2. 加官方QQ群,加官方微信群获取更多资源和帮助
3. 找站长苏飞做网站、商城、CRM、小程序、App、爬虫相关、项目外包等点这里
发表于 2014-4-16 14:40:29 | 显示全部楼层
做一个标记,或者你一个线程取完100条之后把它从数据源Remove掉,失败了就重新放回数据源
发表于 2014-4-16 14:56:49 | 显示全部楼层
还是需要做同步
 楼主| 发表于 2014-4-16 15:07:49 | 显示全部楼层
killse 发表于 2014-4-16 14:40
做一个标记,或者你一个线程取完100条之后把它从数据源Remove掉,失败了就重新放回数据源

试过了,删除了这100条数据不行,因为线程正在执行,没了数据会报错
 楼主| 发表于 2014-4-16 21:45:52 | 显示全部楼层
尝试了把1000条数据分成10个集合数组,但发现运行起来后  集合会报 “未将对象引用设置到对象的实例。”或者是 “超出数组界限”之类的错误。
[C#] 纯文本查看 复制代码
 static void Main(string[] args)
        {
            Thread[] t = new Thread[10];
            ArrayList list = new ArrayList();
            ArrayList[] temp = new ArrayList[10];
            
            for (int i = 0; i < 1000; i++)
            {
                list.Add(i.ToString());
            }
            for (int i = 0; i < 10; i++)
            {
                temp[i] = new ArrayList();
                for (int j = 0; j < 100; j++)
                {
                    temp[i].Add(list[j]);
                } 
                list.RemoveRange(0, 99);
                t[i] = new Thread(new ThreadStart(delegate
                    {
                        for (int k = 0; k < temp[i].Count; k++)
                        {
                            TestFor(temp[i][k].ToString());
                        } 
                    }
                    ));
                t[i].Name = i.ToString();
                t[i].Start();
                Thread.Sleep(1000);
}
            }

 public static void TestFor(string value)
        {
            Console.WriteLine("这是线程"+Thread.CurrentThread.Name+":"+value);
            Thread.Sleep(10);
        }




这里的代码会报 “未将对象引用设置到对象的实例。”或者是 “超出数组界限”之类的错误。 指向的错误是集合数组temp。
估计原因是:每循环开启一个线程后,  然后给数组赋值了,而正在运行的线程里的数组也会变成数组,因此被新值覆盖,所以出现了错误。


请问原因是这样么?


但我感觉如果是这样的话 有点不符合逻辑,线程1 调用的是 数组1,线程2 调用的是 数组2 ,但为什么因为数组2的值变了 ,线程1里的数组1 也变了?他们各自的值不是放到内存里的吗?
 楼主| 发表于 2014-4-16 22:23:12 | 显示全部楼层
但我发现把方法做成循环,线程调用的时候直接传一个数组过去,而不是在线程里循环方法,这样上面的代码就不报错了,奇怪,有大神讲解下么?

[C#] 纯文本查看 复制代码
    static void Main(string[] args)
        {
            Thread[] t = new Thread[10];
            ArrayList list = new ArrayList();
            ArrayList[] temp = new ArrayList[10];
            
            for (int i = 0; i < 1000; i++)
            {
                list.Add(i.ToString());
            }
            for (int i = 0; i <t.Length; i++)
            {
                temp[i] = new ArrayList();
                for (int j = 0; j < 100; j++)
                {
                    temp[i].Add(list[j]);
                } 
                list.RemoveRange(0, 99);
                t[i] = new Thread(new ThreadStart(delegate
                    {
                            TestFor(temp[i]);
                    }
                    ));
                t[i].Name = i.ToString();
                t[i].Start();
                Thread.Sleep(200);
            }
            Console.ReadKey();
           
        }

        public static void TestFor(ArrayList list)
        {
            for (int i = 0; i < 100; i++)
            {
                Console.WriteLine("这是线程" + Thread.CurrentThread.Name + ":" + list[i].ToString());
                Thread.Sleep(10);
            }  
        }

 楼主| 发表于 2014-4-16 22:31:06 | 显示全部楼层
线程之间不应该是互不干扰的吗?像5楼的代码,线程0启动了,为什么会因为数组索引从[0]变成[1]后 ,线程0里的数组也改变成了[1]呢?
@站长苏飞  飞哥有空指点下 谢谢
发表于 2014-4-17 08:22:58 | 显示全部楼层
sandy1231 发表于 2014-4-16 22:31
线程之间不应该是互不干扰的吗?像5楼的代码,线程0启动了,为什么会因为数组索引从[0]变成[1]后 ,线程0里 ...

线程是没有影响的,但是线程调用的同一个变量或者是对象是肯定会影响的啊,因为他们是同一个,所以会根据一个的改变而都改变
 楼主| 发表于 2014-4-17 10:18:43 | 显示全部楼层
站长苏飞 发表于 2014-4-17 08:22
线程是没有影响的,但是线程调用的同一个变量或者是对象是肯定会影响的啊,因为他们是同一个,所以会根据 ...

但为什么如果像下面代码这样,直接传整个数组过去 ,他的值又不会被覆盖掉呢?

[C#] 纯文本查看 复制代码
static void Main(string[] args)
    {
        Thread[] t = new Thread[10];
        ArrayList list = new ArrayList();
        ArrayList[] temp = new ArrayList[10];
         
        for (int i = 0; i < 1000; i++)
        {
            list.Add(i.ToString());
        }
        for (int i = 0; i <t.Length; i++)
        {
            temp[i] = new ArrayList();
            for (int j = 0; j < 100; j++)
            {
                temp[i].Add(list[j]);
            } 
            list.RemoveRange(0, 99);
            t[i] = new Thread(new ThreadStart(delegate
                {

////循环后这里的temp[i]值不会被覆盖掉
                        TestFor(temp[i]);

                }
                ));
            t[i].Name = i.ToString();
            t[i].Start();
            Thread.Sleep(200);
        }
        Console.ReadKey();
        
    }
 
    public static void TestFor(ArrayList list)
    {
        for (int i = 0; i < 100; i++)
        {
            Console.WriteLine("这是线程" + Thread.CurrentThread.Name + ":" + list[i].ToString());
            Thread.Sleep(10);
        }  
    }

发表于 2014-4-17 10:38:46 | 显示全部楼层
temp在方法&#160;public static void TestFor(ArrayList list)
&#160;&#160;&#160;&#160;{
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;for (int i = 0; i < 100; i++)
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;{
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Console.WriteLine("这是线程" + Thread.CurrentThread.Name + ":" + list[i].ToString());
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Thread.Sleep(10);
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;}&#160;
&#160;&#160;&#160;&#160;}并没有赋值肯定不会改变啊
您需要登录后才可以回帖 登录 | 马上注册

本版积分规则

QQ|手机版|小黑屋|手机版|联系我们|关于我们|广告合作|苏飞论坛 ( 豫ICP备18043678号-2)

GMT+8, 2024-11-24 05:08

© 2014-2021

快速回复 返回顶部 返回列表