• 封装多线程处理大量数据操作(二)


    接上文:封装多线程处理大量数据操作(一)

    我们需要解决WaitAny和取得异步执行的返回值的问题。地球人都知道Thread和ThreadPool接受的委托都是没有返回值的。要想取的返回值,我们就得自己动手了,我们需要构造一个AsyncContext类,由这个类来保存异步执行的状态以并存储返回值。

    代码如下:

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Collections;
    using System.Threading;
    using System.Diagnostics;
    
    namespace AppUtility
    {
    public delegate object DoGetObjTask(object state);
        public static class AsyncHelper
        {
            /// <summary>
            /// 执行多线程操作任务
            /// </summary>
            /// <param name="dataCollection">多线程操作的数据集合</param>
            /// <param name="threadCn">分多少个线程来做</param>
            /// <param name="processItemMethod">处理数据集合中单个数据使用的处理方法</param>
            public static void DoAsync(IList dataCollection, int threadCn, WaitCallback processItemMethod)
            {
                DoAsync(dataCollection, threadCn, processItemMethod, true);
            }
    
    
            /// <summary>
            /// 执行多线程操作任务
            /// </summary>
            /// <param name="dataCollection">多线程操作的数据集合</param>
            /// <param name="threadCn">分多少个线程来做</param>
            /// <param name="processItemMethod">处理数据集合中单个数据使用的处理方法</param>
            /// <param name="needWaitAll">是否需要等待所有线程执行完毕才返回,为true时会等待所有线程执行完毕,否则则是在有一个线程执行完毕就返回</param>
            public static void DoAsync(IList dataCollection, int threadCn, DoGetObjTask processItemMethod, bool needWaitAll, out Hashtable processResult)
            {
                DoAsyncPrivate(dataCollection, threadCn, null, processItemMethod, needWaitAll, true, out processResult);
            }
    
            /// <summary>
            /// 执行多线程操作任务
            /// </summary>
            /// <param name="dataCollection">多线程操作的数据集合</param>
            /// <param name="threadCn">分多少个线程来做</param>
            /// <param name="processItemMethod">处理数据集合中单个数据使用的处理方法</param>
            /// <param name="needWaitAll">是否需要等待所有线程执行完毕才返回,为true时会等待所有线程执行完毕,否则则是在有一个线程执行完毕就返回</param>
            public static void DoAsync(IList dataCollection, int threadCn, DoGetObjTask processItemMethod, out Hashtable processResult)
            {
                DoAsyncPrivate(dataCollection, threadCn, null, processItemMethod, true, true, out processResult);
            }
    
            /// <summary>
            /// 执行多线程操作任务
            /// </summary>
            /// <param name="dataCollection">多线程操作的数据集合</param>
            /// <param name="threadCn">分多少个线程来做</param>
            /// <param name="processItemMethod">处理数据集合中单个数据使用的处理方法</param>
            /// <param name="needWaitAll">是否需要等待所有线程执行完毕才返回,为true时会等待所有线程执行完毕,否则则是在有一个线程执行完毕就返回</param>
            public static void DoAsync(IList dataCollection, int threadCn, WaitCallback processItemMethod, bool needWaitAll)
            {
                Hashtable hash;
                DoAsyncPrivate(dataCollection, threadCn, processItemMethod, null, needWaitAll, false, out hash);
            }
    
            private static void DoAsyncPrivate(IList dataCollection, int threadCn, WaitCallback processItemMethod, DoGetObjTask getObjMethod, bool needWaitAll, bool hasReturnValue, out Hashtable processResult)
            {
                if (dataCollection == null) throw new ArgumentNullException("dataCollection");
    
                if (threadCn >= 64 || threadCn < 2)
                {
                    throw new ArgumentOutOfRangeException("threadCn", "threadCn 参数必须在2和64之间");
                }
    
                if (threadCn > dataCollection.Count) threadCn = dataCollection.Count;
    
                IList[] colls = new ArrayList[threadCn];
    
                DataWithStateList dataWithStates = new DataWithStateList();
                AutoResetEvent[] evts = new AutoResetEvent[threadCn];
    
                for (int i = 0; i < threadCn; i++)
                {
                    colls[i] = new ArrayList();
                    evts[i] = new AutoResetEvent(false);
                }
    
                for (int i = 0; i < dataCollection.Count; i++)
                {
                    object obj = dataCollection[i];
                    int threadIndex = i % threadCn;
                    colls[threadIndex].Add(obj);
                    dataWithStates.Add(new DataWithState(obj, ProcessState.WaitForProcess));
                }
    
                AsyncContext context = AsyncContext.GetContext(threadCn, dataWithStates, needWaitAll, hasReturnValue, processItemMethod, getObjMethod);
    
                for (int i = 0; i < threadCn; i++)
                {
                    ThreadPool.QueueUserWorkItem(DoPrivate, new object[] { 
                        colls[i],context,evts[i]
                    });
                }
    
                if (needWaitAll)
                {
                    WaitHandle.WaitAll(evts);
                }
                else
                {
                    WaitHandle.WaitAny(evts);
                    context.SetBreakSignal();
                }
                processResult = context.ProcessResult;
            }
    
            private class AsyncContext
            {
                static public AsyncContext GetContext(
                    int threadCn,
                    DataWithStateList dataWithStates,
                    bool needWaitAll,
                    bool hasReturnValue,
                    WaitCallback processItemMethod,
                    DoGetObjTask hasReturnValueMethod
                    )
                {
                    AsyncContext context = new AsyncContext();
                    context.ThreadCount = threadCn;
                    context.DataWithStates = dataWithStates;
                    context.NeedWaitAll = needWaitAll;
                    if (hasReturnValue)
                    {
                        Hashtable processResult = Hashtable.Synchronized(new Hashtable());
                        context.ProcessResult = processResult;
                        context.HasReturnValueMethod = hasReturnValueMethod;
                    }
                    else
                    {
                        context.VoidMethod = processItemMethod;
                    }
                    context.HasReturnValue = hasReturnValue;
                    return context;
                }
    
                internal int ThreadCount;
    
                internal DataWithStateList DataWithStates;
    
                internal bool NeedWaitAll;
    
                internal bool HasReturnValue;
    
                internal WaitCallback VoidMethod;
    
                internal DoGetObjTask HasReturnValueMethod;
    
                private bool _breakSignal;
    
                private Hashtable _processResult;
    
                internal Hashtable ProcessResult
                {
                    get { return _processResult; }
                    set { _processResult = value; }
                }
    
                internal void SetReturnValue(object obj, object result)
                {
                    lock (_processResult.SyncRoot)
                    {
                        _processResult[obj] = result;
                    }
                }
    
                internal void SetBreakSignal()
                {
                    if (NeedWaitAll) throw new NotSupportedException("设定为NeedWaitAll时不可设置BreakSignal");
    
                    _breakSignal = true;
                }
    
                internal bool NeedBreak
                {
                    get
                    {
                        return !NeedWaitAll && _breakSignal;
                    }
                }
    
                internal void Exec(object obj)
                {
                    if (HasReturnValue)
                    {
                        SetReturnValue(obj, HasReturnValueMethod(obj));
                    }
                    else
                    {
                        VoidMethod(obj);
                    }
                    DataWithStates.SetState(obj, ProcessState.Processed);
                }
            }
    
            private enum ProcessState : byte
            {
                WaitForProcess = 0,
                Processing = 1,
                Processed = 2
            }
    
            private class DataWithStateList : List<DataWithState>
            {
                public void SetState(object obj, ProcessState state)
                {
                    lock (((ICollection)this).SyncRoot)
                    {
                        DataWithState dws = this.Find(delegate(DataWithState i) { return Object.Equals(i.Data, obj); });
    
                        if (dws != null)
                        {
                            dws.State = state;
                        }
                    }
                }
    
                public ProcessState GetState(object obj)
                {
                    lock (((ICollection)this).SyncRoot)
                    {
                        DataWithState dws = this.Find(delegate(DataWithState i) { return Object.Equals(i.Data, obj); });
                        return dws.State;
                    }
                }
    
                private int GetCount(ProcessState state)
                {
                    List<DataWithState> datas = this.FindAll(delegate(DataWithState i) { return i.State == state; });
                    if (datas == null) return 0;
                    return datas.Count;
                }
    
                public int WaitForDataCount
                {
                    get
                    {
                        return GetCount(ProcessState.WaitForProcess);
                    }
                }
    
                internal object GetWaitForObject()
                {
                    lock (((ICollection)this).SyncRoot)
                    {
                        DataWithState dws = this.Find(delegate(DataWithState i) { return i.State == ProcessState.WaitForProcess; });
                        if (dws == null) return null;
                        dws.State = ProcessState.Processing;
                        return dws.Data;
                    }
                }
    
                internal bool IsWaitForData(object obj, bool setState)
                {
                    lock (((ICollection)this).SyncRoot)
                    {
                        DataWithState dws = this.Find(delegate(DataWithState i) { return i.State == ProcessState.WaitForProcess; });
    
                        if (setState && dws != null) dws.State = ProcessState.Processing;
    
                        return dws != null;
                    }
                }
            }
    
            private class DataWithState
            {
                public readonly object Data;
                public ProcessState State;
    
                public DataWithState(object data, ProcessState state)
                {
                    Data = data;
                    State = state;
                }
            }
    
            private static int _threadNo = 0;
    
            private static void DoPrivate(object state)
            {
                object[] objs = state as object[];
    
                IList datas = objs[0] as IList;
                AsyncContext context = objs[1] as AsyncContext;
                AutoResetEvent evt = objs[2] as AutoResetEvent;
    
                DataWithStateList objStates = context.DataWithStates;
    
    #if DEBUG
                Thread.CurrentThread.Name = "Thread " + _threadNo;
    
                Interlocked.Increment(ref _threadNo);
                string threadName = Thread.CurrentThread.Name + "[" + Thread.CurrentThread.ManagedThreadId + "]";
                Trace.WriteLine("线程ID:" + threadName);
    #endif
                if (datas != null)
                {
                    for (int i = 0; i < datas.Count; i++)
                    {
                        if (context.NeedBreak)
                        {
    #if DEBUG
                            Trace.WriteLine("线程" + threadName + "未执行完跳出");
    #endif
                            break;
                        }
                        object obj = datas[i];
                        if (objStates.IsWaitForData(obj, true))
                        {
                            if (context.NeedBreak)
                            {
    #if DEBUG
                                Trace.WriteLine("线程" + threadName + "未执行完跳出");
    #endif
                                break;
                            }
    
                            context.Exec(obj);
    
    #if DEBUG
                            Trace.WriteLine(string.Format("线程{0}处理{1}", threadName, obj));
    #endif
                        }
                    }
                }
    
                if (context.NeedWaitAll)
                {
                    //如果执行完当前进程的数据,还要查看剩下多少没有做,如果还剩下超过ThreadCount个没有做
                    while (objStates.WaitForDataCount > context.ThreadCount)
                    {
                        if (context.NeedBreak) break;
    
                        object obj = objStates.GetWaitForObject();
                        if (obj != null && objStates.IsWaitForData(obj, false))
                        {
                            if (context.NeedBreak)
                            {
    #if DEBUG
                                Trace.WriteLine("线程" + threadName + "未执行完跳出");
    #endif
                                break;
                            }
    
                            context.Exec(obj);
    
    #if DEBUG
                            Trace.WriteLine(string.Format("线程{0}执行另一个进程的数据{1}", threadName, obj));
    #endif
                        }
                    }
                }
    
                evt.Set();
            }
    
    
        }
    }
    
    

    如何使用AsyncHelper类,请看下面的测试代码:

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Diagnostics;
    using AppUtility;
    using System.IO;
    using System.Collections;
    using System.Threading;
    
    namespace ConsoleApplication2
    {
        class Program
        {
            static void Main(string[] args)
            {
                Stopwatch sw = new Stopwatch();
                sw.Start();
                /*
                List<string> testFiles = new List<string>();
                for (int i = 0; i < 100; i++)
                {
                    testFiles.Add("D:\\test\\async\\file_" + i.ToString() + ".log");
                }
                AsyncHelper.DoAsync(testFiles, 10, WriteFile);
    
                Console.WriteLine("异步写耗时"+sw.ElapsedMilliseconds + "ms");
                */
    
                List<string> testFiles = new List<string>();
                for (int i = 0; i < 200; i++)
                {
                    testFiles.Add("D:\\test\\async\\file_" + i.ToString() + ".log");
                }
                
                Hashtable result;
    
                AsyncHelper.DoAsync(testFiles, 20, WriteFileAndReturnRowCount,false,out result);
    
                Console.WriteLine("异步写耗时" + sw.ElapsedMilliseconds + "ms");
    
                Thread.Sleep(10);
    
                if (result != null)
                {
                    foreach (object key in result.Keys)
                    {
                        Console.WriteLine("{0}={1}",  key,result[key]);
                    }
    
                }
    
                sw.Reset();
                sw.Start();
                for (int i = 0; i < 200; i++)
                {
                    WriteFile("D:\\test\\sync\\file_" + i.ToString() + ".log");
                }
    
                Console.WriteLine("同步写耗时" + sw.ElapsedMilliseconds + "ms");
    
                Console.Read();
            }
    
            static void WriteFile(object objFilePath)
            {
                string filePath = (string)objFilePath;
                string dir = Path.GetDirectoryName(filePath);
                if (!Directory.Exists(dir))
                {
                    Directory.CreateDirectory(dir);
                }
    
                //Random r = new Random(DateTime.Now.Minute);
                int rowCn = 10000;
                using (StreamWriter writer = new StreamWriter(filePath, false, Encoding.Default))
                {
                    for (int i = 0; i < rowCn; i++) writer.WriteLine(Guid.NewGuid());
                }
            }
    
            static object WriteFileAndReturnRowCount(object objFilePath)
            {
                string filePath = (string)objFilePath;
                string dir = Path.GetDirectoryName(filePath);
                if (!Directory.Exists(dir))
                {
                    Directory.CreateDirectory(dir);
                }
    
                //Random r = new Random(DateTime.Now.Minute);
                int rowCn = 10000;
                using (StreamWriter writer = new StreamWriter(filePath, false, Encoding.Default))
                {
                    for (int i = 0; i < rowCn ; i++) writer.WriteLine(Guid.NewGuid());
                }
                return DateTime.Now.ToLongTimeString();
            }
        }
    }
    
    Sorry,代码太多,文字太少。发个牢骚,代码写完之后,再写思路是一件痛苦的事情!
  • 相关阅读:
    SUSE10 SP2/SP3 无规律死机故障解决
    随机铃声
    linux添加开机启动项
    SUSE Linux ShutdownManager issue
    linux添加开机启动项
    两个正在运行的activity之间的通信
    android 获取屏幕大小
    Linux开机启动过程分析
    grid的宽度设为100%问题
    动态处理editGridPanel
  • 原文地址:https://www.cnblogs.com/yukaizhao/p/system_threading_threadpool_AsyncHelper_2.html
Copyright © 2020-2023  润新知