首页 新闻 会员 周边 捐助

.net关于多线程的问题

0
[已解决问题] 解决于 2019-05-23 09:42

对于多线程不是很懂,面试的时候遇到一个多线程的题,不会做,分享出来,懂的大佬指点一下,谢谢

建一个winform窗体,在窗体中放上一个开始按钮,一个停止按钮,一个文本框,在窗体中声明一个List<int>类型的属性,点击开始按钮后开启10个线程,所有线程同时不间断的给List<int>集合中添加1-10000之间的随机数,要求添加List<int>集合中的数字不能重复,并且实时在文本框中显示集合的长度,当集合List<int>的长度等于1000时自动停止所有线程,如果中途点击停止按钮也停止所有线程,点击开始又继续执行。

爱写随记的代码搬运工的主页 爱写随记的代码搬运工 | 菜鸟二级 | 园豆:208
提问于:2019-05-22 15:14
< >
分享
最佳答案
0

添加的集合写成一个方法,所有线程来调用,方法里加lock,一边add一边检查是否到了1000,就行了,至于重复的问题,不同的线程add的数据分开来就行了,比如第一个线程1~1000,第二个线程 1001~2000,以此类推
至于中途停止的问题,加个全局变量就行了,通过检查变量break出for循环就好了

奖励园豆:5
jqw2009 | 老鸟四级 |园豆:2341 | 2019-05-22 15:33

谢谢,知道怎么停止了

爱写随记的代码搬运工 | 园豆:208 (菜鸟二级) | 2019-05-22 15:44
其他回答(3)
0

你的问题是什么?

会长 | 园豆:12463 (专家六级) | 2019-05-22 15:25

我写了一个方法,准备创建十个线程调用方法,但是不知道怎么实现同时停止和继续。
Random rand = new Random();
int count = 0;
List<int> lis = new List<int>();
public void method()
{
while (true)
{
int num = rand.Next(0, 10001);
if (!lis.Contains(num))
{
lis.Add(num);
if (count < 1000)
{
count++;
textBox1.Text = count.ToString();
}
Thread.Sleep(100);
}
}
}
下面时开始按钮的事件,我循环开启了十个线程,但是停止按钮不知道怎么实现功能
private void button1_Click(object sender, EventArgs e)
{
for (int i = 0; i < 10; i++)
{
Thread t = new Thread(new ThreadStart(method));
t.Start();
}
}

0
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace CSharpFundamental
{
    class MultipleThreadCompete
    {
        List<int> numList = new List<int>();
        CancellationTokenSource cts = new CancellationTokenSource();
        private const int ENDNUM = 10000;

        internal void DoTheCompeteSecond()
        {
            Task[] tasks = new Task[10];

            for (int i = 0; i < 10; ++i)
            {
                int num = i;
                tasks[i] = Task.Factory.StartNew(() => AddNumToList(num, cts), cts.Token);
            }

            Task.WaitAny(tasks);
        }

        private int GetTheListCount()
        {
            return numList.Count;
        }

        private void AddToList(int num)
        {
            if (numList.Count == ENDNUM)
            {
                return;
            }

            numList.Add(num);
        }

        private void AddNumToList(object state, CancellationTokenSource cts)
        {
            Console.WriteLine("This is the {0} thread,Current ThreadId={1}",
                state,
                Thread.CurrentThread.ManagedThreadId);

            while (!cts.Token.IsCancellationRequested)
            {
                if (GetTheListCount() == ENDNUM)
                {
                    cts.Cancel();
                    Console.WriteLine("Current Thread Id={0},Current Count={1}",
                        Thread.CurrentThread.ManagedThreadId,
                        GetTheListCount());

                    break;
                }
                var insertNum = GenerateInt32Num();
                if (numList.Contains(insertNum))
                {
                    insertNum = GenerateInt32Num();
                }

                AddToList(insertNum);
            }
        }

        private int GenerateInt32Num()
        {
            Random random = new Random();

            return random.Next(1, 10000);
        }
    }
}

Result:

写了一个较为简易 实现的 DEMO,用CancellationTokenSource 来实现取消Task, 使用 Task.WaitAny 用来返回任意执行完毕的 Task, 本来用 Parallel.Invoke 线程池来实现,但是不满足你的需求,没有创建 10 个线程,欢迎指点,交流。

BUTTERAPPLE | 园豆:3190 (老鸟四级) | 2019-05-22 22:47
0

楼上使用Random皆不是正确使用做法。
并发操作List也是错误的。
稍微严格点说用Task也是错误的。
只需要Thread 然后在循环中用条件判断就可完成暂停,至于取不重复的办法就很多了。

花飘水流兮 | 园豆:13615 (专家六级) | 2019-05-23 09:25

愿闻其详

支持(0) 反对(0) BUTTERAPPLE | 园豆:3190 (老鸟四级) | 2019-05-23 10:10

@BUTTERAPPLE:
1.Random用一个实例;
2.List并发写->你设想这不是原子操作,那么其中2个“同时”取到的是一个地址引用会是什么结果;
3.池不等同于线程数量,池可以设置是2

支持(0) 反对(0) 花飘水流兮 | 园豆:13615 (专家六级) | 2019-05-23 10:18

@BUTTERAPPLE: 还有楼上不是只是指你,随便看了一下,包括顶顶上的code

支持(0) 反对(0) 花飘水流兮 | 园豆:13615 (专家六级) | 2019-05-23 10:21

@花飘水流兮:劳烦赐教,谢谢。我去研究一下。

支持(0) 反对(0) BUTTERAPPLE | 园豆:3190 (老鸟四级) | 2019-05-23 10:37

@花飘水流兮: 使用 1 个线程池,让线程从线程池中自己分配,修改了一下代码

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;

namespace CSharpFundamental
{
    class MultipleThreadCompete
    {
        List<int> numList = new List<int>();
        Random random = new Random();
        CancellationTokenSource cts = new CancellationTokenSource();
        private const int ENDNUM = 1000000;

        ReaderWriterLockSlim rwls = new ReaderWriterLockSlim();

        internal void DoTheCompeteSecond()
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            Task[] tasks = new Task[100];

            for (int i = 0; i < 100; ++i)
            {
                int num = i;
                tasks[i] = Task.Run(() => AddNumToList(num, cts), cts.Token);
            }

            Task.WaitAny(tasks);

            Console.WriteLine("ExecuteTime={0}", sw.ElapsedMilliseconds / 1000);
        }

        private int GetTheListCount()
        {
            return numList.Count;
        }

        private void AddToList(int num)
        {
            rwls.EnterReadLock();
            if (numList.Count == ENDNUM)
                return;
            rwls.ExitReadLock();

            rwls.EnterWriteLock();
            numList.Add(num);
            rwls.ExitWriteLock();
        }

        private void AddNumToList(object state, CancellationTokenSource cts)
        {
            Console.WriteLine("This is the {0} thread,Current ThreadId={1}",
                state,
                Thread.CurrentThread.ManagedThreadId);

            while (!cts.Token.IsCancellationRequested)
            {
                try
                {
                    rwls.EnterReadLock();
                    if (numList.Count == ENDNUM)
                    {
                        cts.Cancel();
                        Console.WriteLine("Current Thread Id={0},Current Count={1}",
                            Thread.CurrentThread.ManagedThreadId,
                            GetTheListCount());
                        break;
                    }
                }
                finally
                {
                    rwls.ExitReadLock();
                }

                var insertNum = GenerateInt32Num();
                if (numList.Contains(insertNum))
                {
                    insertNum = GenerateInt32Num();
                }

                AddToList(insertNum);
            }
        }

        private int GenerateInt32Num()
        {
            return random.Next(1, ENDNUM);
        }
    }
}

支持(0) 反对(0) BUTTERAPPLE | 园豆:3190 (老鸟四级) | 2019-05-23 12:27

@花飘水流兮: ThreadPool.SetMinThreads(CURRENT_THREAD_COUNT, CURRENT_THREAD_COUNT); 分配最小线程即可实现所需要的线程数,但不推荐,即使建了那么多,可能更多的时间用在了 CPU 切换 上下文上了

支持(0) 反对(0) BUTTERAPPLE | 园豆:3190 (老鸟四级) | 2019-05-23 12:54
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册