当前位置: 首页 > news >正文

大庆免费网站建设公司网站制作成功案例

大庆免费网站建设公司,网站制作成功案例,大连建设网网址,连云港做网站哪家好从此图中我们会发现 .NET 与C# 的每个版本发布都是有一个“主题”。即#xff1a;C#1.0托管代码→C#2.0泛型→C#3.0LINQ→C#4.0动态语言→C#5.0异步编程。现在我为最新版本的“异步编程”主题写系列分享#xff0c;期待你的查看及点评。 传送门#xff1a;异步编程系列目录…… 从此图中我们会发现 .NET 与C# 的每个版本发布都是有一个“主题”。即C#1.0托管代码→C#2.0泛型→C#3.0LINQ→C#4.0动态语言→C#5.0异步编程。现在我为最新版本的“异步编程”主题写系列分享期待你的查看及点评。 传送门异步编程系列目录…… 开始《异步编程使用线程池管理线程》 示例程序异步编程使用线程池管理线程.rar 如今的应用程序越来越复杂我们常常需要使用《异步编程线程概述及使用》中提到的多线程技术来提高应用程序的响应速度。这时我们频繁的创建和销毁线程来让应用程序快速响应操作这频繁的创建和销毁无疑会降低应用程序性能我们可以引入缓存机制解决这个问题此缓存机制需要解决如缓存的大小问题、排队执行任务、调度空闲线程、按需创建新线程及销毁多余空闲线程……如今微软已经为我们提供了现成的缓存机制线程池 线程池原自于对象池在详细解说明线程池前让我们先来了解下何为对象池。 对象池 在系统设计中我们尝尝会使用到“池”的概念。Eg数据库连接池socket连接池线程池组件队列。“池”可以节省对象重复创建和初始化所耗费的时间。对那些被系统频繁请求和使用的对象使用此机制可以提高系统运行性能。 “池”是一种“以空间换时间”的做法我们在内存中保存一系列整装待命的对象供人随时差遣。与系统效率相比这些对象所占用的内存空间太微不足道了。 流程图 对于对象池的清理通常设计两种方式 1)         手动清理即主动调用清理的方法。 2)         自动清理即通过System.Threading.Timer来实现定时清理。 关键实现代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 public sealed class ObjectPoolT where T : ICacheObjectProxyT {     // 最大容量     private Int32 m_maxPoolCount 30;     // 最小容量     private Int32 m_minPoolCount 5;     // 已存容量     private Int32 m_currentCount;     // 空闲被用 对象列表     private Hashtable m_listObjects;     // 最大空闲时间     private int maxIdleTime 120;     // 定时清理对象池对象     private Timer timer null;       /// summary     /// 创建对象池     /// /summary     /// param namemaxPoolCount最小容量/param     /// param nameminPoolCount最大容量/param     /// param namecreate_params待创建的实际对象的参数/param     public ObjectPool(Int32 maxPoolCount, Int32 minPoolCount, Object[] create_params){ }       /// summary     /// 获取一个对象实例     /// /summary     /// returns返回内部实际对象若返回null则线程池已满/returns     public T GetOne(){ }       /// summary     /// 释放该对象池     /// /summary     public void Dispose(){ }       /// summary     /// 将对象池中指定的对象重置并设置为空闲状态     /// /summary     public void ReturnOne(T obj){ }       /// summary     /// 手动清理对象池     /// /summary     public void ManualReleaseObject(){ }       /// summary     /// 自动清理对象池对大于 最小容量 的空闲对象进行释放     /// /summary     private void AutoReleaseObject(Object obj){ } } 通过对“对象池”的一个大体认识能帮我们更快理解线程池。 线程池ThreadPool类详解 ThreadPool静态类为应用程序提供一个由系统管理的辅助线程池从而使您可以集中精力于应用程序任务而不是线程管理。每个进程都有一个线程池,一个Process中只能有一个实例它在各个应用程序域AppDomain是共享的。 在内部线程池将自己的线程划分工作者线程(辅助线程)和I/O线程。前者用于执行普通的操作后者专用于异步IO比如文件和网络请求注意分类并不说明两种线程本身有差别内部依然是一样的。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public static class ThreadPool {     // 将操作系统句柄绑定到System.Threading.ThreadPool。     public static bool BindHandle(SafeHandle osHandle);       // 检索由ThreadPool.GetMaxThreads(Int32,Int32)方法返回的最大线程池线程数和当前活动线程数之间的差值。     public static void GetAvailableThreads(out int workerThreads             , out int completionPortThreads);       // 设置和检索可以同时处于活动状态的线程池请求的数目。     // 所有大于此数目的请求将保持排队状态直到线程池线程变为可用。     public static bool SetMaxThreads(int workerThreads, int completionPortThreads);     public static void GetMaxThreads(out int workerThreads, out int completionPortThreads);     // 设置和检索线程池在新请求预测中维护的空闲线程数。     public static bool SetMinThreads(int workerThreads, int completionPortThreads);     public static void GetMinThreads(out int workerThreads, out int completionPortThreads);       // 将方法排入队列以便执行并指定包含该方法所用数据的对象。此方法在有线程池线程变得可用时执行。     public static bool QueueUserWorkItem(WaitCallback callBack, object state);     // 将重叠的 I/O 操作排队以便执行。如果成功地将此操作排队到 I/O 完成端口则为 true否则为 false。     // 参数overlapped:要排队的System.Threading.NativeOverlapped结构。     public static bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped);     // 将指定的委托排队到线程池但不会将调用堆栈传播到工作者线程。     public static bool UnsafeQueueUserWorkItem(WaitCallback callBack, object state);       // 注册一个等待Threading.WaitHandle的委托并指定一个 32 位有符号整数来表示超时值以毫秒为单位。     // executeOnlyOnce如果为 true表示在调用了委托后线程将不再在waitObject参数上等待     // 如果为 false表示每次完成等待操作后都重置计时器直到注销等待。     public static RegisteredWaitHandle RegisterWaitForSingleObject(             WaitHandle waitObject             , WaitOrTimerCallback callBack, object state,             Int millisecondsTimeOutInterval, bool executeOnlyOnce);     public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject(               WaitHandle waitObject             , WaitOrTimerCallback callBack             , object state             , int millisecondsTimeOutInterval             , bool executeOnlyOnce);     …… } 1.         线程池线程数 1)         使用GetMaxThreads()和SetMaxThreads()获取和设置最大线程数 可排队到线程池的操作数仅受内存的限制而线程池限制进程中可以同时处于活动状态的线程数默认情况下限制每个 CPU 可以使用 25 个工作者线程和 1,000 个 I/O 线程(根据机器CPU个数和.net framework版本的不同这些数据可能会有变化)所有大于此数目的请求将保持排队状态直到线程池线程变为可用。 不建议更改线程池中的最大线程数 a)         将线程池大小设置得太大可能会造成更频繁的执行上下文切换及加剧资源的争用情况。 b)         其实FileStream的异步读写,异步发送接受Web请求,System.Threading.Timer定时器,甚至使用delegate的beginInvoke都会默认调用 ThreadPool,也就是说不仅你的代码可能使用到线程池,框架内部也可能使用到。 c)         一个应用程序池是一个独立的进程,拥有一个线程池,应用程序池中可以有多个WebApplication,每个运行在一个单独的AppDomain中,这些WebApplication公用一个线程池。 2)         使用GetMinThreads()和SetMinThreads()获取和设置最小空闲线程数 为避免向线程分配不必要的堆栈空间线程池按照一定的时间间隔创建新的空闲线程该间隔为半秒。所以如果最小空闲线程数设置的过小在短期内执行大量任务会因为创建新空闲线程的内置延迟导致性能瓶颈。最小空闲线程数默认值等于机器上的CPU核数并且不建议更改最小空闲线程数。 在启动线程池时线程池具有一个内置延迟用于启用最小空闲线程数以提高应用程序的吞吐量。 在线程池运行中对于执行完任务的线程池线程不会立即销毁而是返回到线程池线程池会维护最小的空闲线程数(即使应用程序所有线程都是空闲状态)以便队列任务可以立即启动。超过此最小数目的空闲线程一段时间没事做后会自己醒来终止自己以节省系统资源。 3)         静态方法GetAvailableThreads() 通过静态方法GetAvailableThreads()返回的线程池线程的最大数目和当前活动数目之间的差值即获取线程池中当前可用的线程数目 4)         两个参数 方法GetMaxThreads()、SetMaxThreads()、GetMinThreads()、SetMinThreads()、GetAvailableThreads()钧包含两个参数。参数workerThreads指工作者线程参数completionPortThreads指异步 I/O 线程。 2.         排队工作项 通过调用 ThreadPool.QueueUserWorkItem 并传递 WaitCallback 委托来使用线程池。也可以通过使用 ThreadPool.RegisterWaitForSingleObject 并传递 WaitHandle在向其发出信号或超时时它将引发对由 WaitOrTimerCallback 委托包装的方法的调用来将与等待操作相关的工作项排队到线程池中。若要取消等待操作即不再执行WaitOrTimerCallback委托可调用RegisterWaitForSingleObject()方法返回的RegisteredWaitHandle的 Unregister 方法。 如果您知道调用方的堆栈与在排队任务执行期间执行的所有安全检查不相关则还可以使用不安全的方法 ThreadPool.UnsafeQueueUserWorkItem 和 ThreadPool.UnsafeRegisterWaitForSingleObject。QueueUserWorkItem 和 RegisterWaitForSingleObject 都会捕获调用方的堆栈此堆栈将在线程池线程开始执行任务时合并到线程池线程的堆栈中。如果需要进行安全检查则必须检查整个堆栈但它还具有一定的性能开销。使用“不安全的”方法调用并不会提供绝对的安全但它会提供更好的性能。 3.         在一个内核构造可用时调用一个方法 让一个线程不确定地等待一个内核对象进入可用状态这对线程的内存资源来说是一种浪费。ThreadPool.RegisterWaitForSingleObject()为我们提供了一种方式在一个内核对象变得可用的时候调用一个方法。 使用需注意 1)         WaitOrTimerCallback委托参数该委托接受一个名为timeOut的Boolean参数。如果 WaitHandle 在指定时间内没有收到信号即超时则为 true否则为 false。回调方法可以根据timeOut的值来针对性地采取措施。 2)         名为executeOnlyOnce的Boolean参数。传true则表示线程池线程只执行回调方法一次若传false则表示内核对象每次收到信号线程池线程都会执行回调方法。等待一个AutoResetEvent对象时这个功能尤其有用。 3)         RegisterWaitForSingleObject()方法返回一个RegisteredWaitHandle对象的引用。这个对象标识了线程池正在它上面等待的内核对象。我们可以调用它的Unregister(WaitHandle waitObject)方法取消由RegisterWaitForSingleObject()注册的等待操作(即WaitOrTimerCallback委托不再执行)。Unregister(WaitHandle waitObject)的WaitHandle参数表示成功取消注册的等待操作后线程池会向此对象发出信号(set())若不想收到此通知可以传递null。 示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 private static void Example_RegisterWaitForSingleObject() {     // 加endWaitHandle的原因如果执行过快退出方法会导致一些东西被释放造成排队的任务不能执行原因还在研究     AutoResetEvent endWaitHandle new AutoResetEvent(false);       AutoResetEvent notificWaitHandle new AutoResetEvent(false);     AutoResetEvent waitHandle new AutoResetEvent(false);     RegisteredWaitHandle registeredWaitHandle ThreadPool.RegisterWaitForSingleObject(         waitHandle,         (Object state, bool timedOut)         {             if (timedOut)                 Console.WriteLine(RegisterWaitForSingleObject因超时而执行);             else                 Console.WriteLine(RegisterWaitForSingleObject收到WaitHandle信号);         },         null, TimeSpan.FromSeconds(2), true      );       // 取消等待操作即不再执行WaitOrTimerCallback委托     registeredWaitHandle.Unregister(notificWaitHandle);       // 通知     ThreadPool.RegisterWaitForSingleObject(         notificWaitHandle,         (Object state, bool timedOut)         {             if (timedOut)                 Console.WriteLine(第一个RegisterWaitForSingleObject没有调用Unregister());             else                 Console.WriteLine(第一个RegisterWaitForSingleObject调用了Unregister());               endWaitHandle.Set();         },         null, TimeSpan.FromSeconds(4), true      );       endWaitHandle.WaitOne(); } 执行上下文 上一小节中说到线程池最大线程数设置过大可能会造成Windows频繁执行上下文切换降低程序性能。对于大多数园友不会满意这样的回答我和你一样也喜欢“知其然再知其所以然”。 1.         上下文切换中的“上下文”是什么 .NET中上下文太多我最后得出的结论是上下文切换中的上下文专指“执行上下文”。 执行上下文包括安全上下文、同步上下文(System.Threading.SynchronizationContext)、逻辑调用上下文(System.Runtime.Messaging.CallContext)。即安全设置压缩栈、Thread的Principal属性和Windows身份、宿主设置System.Threading.HostExcecutingContextManager以及逻辑调用上下文数据(System.Runtime.Messaging.CallContext的LogicalSetData()和LogicalGetData()方法)。 2.         何时执行“上下文切换” 当一个“时间片”结束时如果Windows决定再次调度同一个线程那么Windows不会执行上下文切换。如果Windows调度了一个不同的线程这时Windows执行线程上下文切换。 3.         “上下文切换”造成的性能影响 当Windows上下文切换到另一个线程时CPU将执行一个不同的线程而之前线程的代码和数据还在CPU的高速缓存中高速缓存使CPU不必经常访问RAMRAM的速度比CPU高速缓存慢得多当Windows上下文切换到一个新线程时这个新线程极有可能要执行不同的代码并访问不同的数据这些代码和数据不在CPU的高速缓存中。因此CPU必须访问RAM来填充它的高速缓存以恢复高速执行状态。但是在其“时间片”执行完后一次新的线程上下文切换又发生了。 上下文切换所产生的开销不会换来任何内存和性能上的收益。执行上下文所需的时间取决于CPU架构和速度即“时间片”的分配。而填充CPU缓存所需的时间取决于系统运行的应用程序、CPU、缓存的大小以及其他各种因素。所以无法为每一次线程上下文切换的时间开销给出一个确定的值甚至无法给出一个估计的值。唯一确定的是如果要构建高性能的应用程序和组件就应该尽可能避免线程上下文切换。 除此之外执行垃圾回收时CLR必须挂起(暂停)所有线程遍历它们的栈来查找根以便对堆中的对象进行标记再次遍历它们的栈(有的对象在压缩期间发生了移动所以要更新它们的根)再恢复所有线程。所以减少线程的数量也会显著提升垃圾回收器的性能。每次使用一个调试器并遇到一个断点Windows都会挂起正在调试的应用程序中的所有线程并在单步执行或运行应用程序时恢复所有线程。因此你用的线程越多调试体验也就越差。 4.         监视Windows上下文切换工具 Windows实际记录了每个线程被上下文切换到的次数。可以使用像Microsoft Spy这样的工具查看这个数据。这个工具是Visual Studio附带的一个小工具(vs按安装路径\Visual Studio 2012\Common7\Tools)如图 5.         执行上下文类详解 在《异步编程线程概述及使用》中我提到了Thread的两个上下文即 1)         CurrentContext        获取线程正在其中执行的当前上下文。主要用于线程内部存储数据。 2)         ExecutionContext    获取一个System.Threading.ExecutionContext对象该对象包含有关当前线程的各种上下文的信息。主要用于线程间数据共享。 其中获取到的System.Threading.ExecutionContext就是本小节要说的“执行上下文”。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public sealed class ExecutionContext : IDisposable, ISerializable {     public void Dispose();     public void GetObjectData(SerializationInfo info, StreamingContext context);       // 此方法对于将执行上下文从一个线程传播到另一个线程非常有用。     public ExecutionContext CreateCopy();     // 从当前线程捕获执行上下文的一个副本。     public static ExecutionContext Capture();     // 在当前线程上的指定执行上下文中运行某个方法。     public static void Run(ExecutionContext executionContext, ContextCallback callback, object state);       // 取消执行上下文在异步线程之间的流动。     public static AsyncFlowControl SuppressFlow();     public static bool IsFlowSuppressed();     // RestoreFlow  撤消以前的 SuppressFlow 方法调用的影响。     // 此方法由 SuppressFlow 方法返回的 AsyncFlowControl 结构的 Undo 方法调用。     // 应使用 Undo 方法而不是 RestoreFlow 方法恢复执行上下文的流动。     public static void RestoreFlow(); } ExecutionContext 类提供的功能让用户代码可以在用户定义的异步点之间捕获和传输此上下文。公共语言运行时(CLR)确保在托管进程内运行时定义的异步点之间一致地传输 ExecutionContext。 每当一个线程(初始线程)使用另一个线程(辅助线程)执行任务时CLR会将前者的执行上下文流向复制到辅助线程注意这个自动流向是单方向的。这就确保了辅助线程执行的任何操作使用的是相同的安全设置和宿主设置。还确保了初始线程的逻辑调用上下文可以在辅助线程中使用。 但执行上下文的复制会造成一定的性能影响。因为执行上下文中包含大量信息而收集所有这些信息再把它们复制到辅助线程要耗费不少时间。如果辅助线程又采用了更多地辅助线程还必须创建和初始化更多的执行上下文数据结构。 所以为了提升应用程序性能我们可以阻止执行上下文的流动。当然这只有在辅助线程不需要或者不访问上下文信息的时候才能进行阻止。 下面给出一个示例为了演示 1)         在线程间共享逻辑调用上下文数据CallContext。 2)         为了提升性能阻止\恢复执行上下文的流动。 3)         在当前线程上的指定执行上下文中运行某个方法。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 private static void Example_ExecutionContext() {     CallContext.LogicalSetData(Name, 小红);     Console.WriteLine(主线程中Name为{0}, CallContext.LogicalGetData(Name));       // 1)   在线程间共享逻辑调用上下文数据CallContext。     Console.WriteLine(1)在线程间共享逻辑调用上下文数据CallContext。);     ThreadPool.QueueUserWorkItem((Object obj)          Console.WriteLine(ThreadPool线程中Name为\{0}\, CallContext.LogicalGetData(Name)));     Thread.Sleep(500);     Console.WriteLine();     // 2)   为了提升性能取消\恢复执行上下文的流动。     ThreadPool.UnsafeQueueUserWorkItem((Object obj)          Console.WriteLine(ThreadPool线程使用Unsafe异步执行方法来取消执行上下文的流动。Name为\{0}\         , CallContext.LogicalGetData(Name)), null);     Console.WriteLine(2)为了提升性能取消/恢复执行上下文的流动。);     AsyncFlowControl flowControl ExecutionContext.SuppressFlow();     ThreadPool.QueueUserWorkItem((Object obj)          Console.WriteLine((取消ExecutionContext流动)ThreadPool线程中Name为\{0}\, CallContext.LogicalGetData(Name)));     Thread.Sleep(500);     // 恢复不推荐使用ExecutionContext.RestoreFlow()     flowControl.Undo();     ThreadPool.QueueUserWorkItem((Object obj)          Console.WriteLine((恢复ExecutionContext流动)ThreadPool线程中Name为\{0}\, CallContext.LogicalGetData(Name)));     Thread.Sleep(500);     Console.WriteLine();     // 3)   在当前线程上的指定执行上下文中运行某个方法。(通过获取调用上下文数据验证)     Console.WriteLine(3)在当前线程上的指定执行上下文中运行某个方法。(通过获取调用上下文数据验证));     ExecutionContext curExecutionContext ExecutionContext.Capture();     ExecutionContext.SuppressFlow();     ThreadPool.QueueUserWorkItem(         (Object obj)         {             ExecutionContext innerExecutionContext obj as ExecutionContext;             ExecutionContext.Run(innerExecutionContext, (Object state)                  Console.WriteLine(ThreadPool线程中Name为\{0}\br                       , CallContext.LogicalGetData(Name)), null);         }         , curExecutionContext      ); } 结果如图 注意 1)         示例中“在当前线程上的指定执行上下文中运行某个方法”代码中必须使用ExecutionContext.Capture()获取当前执行上下文的一个副本。 a)         若直接使用Thread.CurrentThread.ExecutionContext则会报“无法应用以下上下文: 跨 AppDomains 封送的上下文、不是通过捕获操作获取的上下文或已作为 Set 调用的参数的上下文。”错误。 b)         若使用Thread.CurrentThread.ExecutionContext.CreateCopy()会报“只能复制新近捕获(ExecutionContext.Capture())的上下文”。 2)         取消执行上下文流动除了使用ExecutionContext.SuppressFlow()方式外。还可以通过使用ThreadPool的UnsafeQueueUserWorkItem 和 UnsafeRegisterWaitForSingleObject来执行委托方法。原因是不安全的线程池操作不会传输压缩堆栈。每当压缩堆栈流动时托管的主体、同步、区域设置和用户上下文也随之流动。 线程池线程中的异常 线程池线程中未处理的异常将终止进程。以下为此规则的三种例外情况 1. 由于调用了 Abort线程池线程中将引发ThreadAbortException。 2. 由于正在卸载应用程序域线程池线程中将引发AppDomainUnloadedException。 3. 公共语言运行库或宿主进程将终止线程。 何时不使用线程池线程 现在大家都已经知道线程池为我们提供了方便的异步API及托管的线程管理。那么是不是任何时候都应该使用线程池线程呢当然不是我们还是需要“因地制宜”的在以下几种情况下适合于创建并管理自己的线程而不是使用线程池线程 1.         需要前台线程。(线程池线程“始终”是后台线程) 2.         需要使线程具有特定的优先级。(线程池线程都是默认优先级“不建议”进行修改) 3.         任务会长时间占用线程。由于线程池具有最大线程数限制因此大量占用线程池线程可能会阻止任务启动。 4.         需要将线程放入单线程单元(STA)。(所有ThreadPool线程“始终”是多线程单元(MTA)中) 5.         需要具有与线程关联的稳定标识或使某一线程专用于某一任务。 本博文介绍线程池以及其基础对象池ThreadPool类的使用及注意事项如何排队工作项到线程池执行上下文及线程上下文传递问题……  线程池虽然为我们提供了异步操作的便利但是它不支持对线程池中单个线程的复杂控制致使我们有些情况下会直接使用Thread。并且它对“等待”操作、“取消”操作、“延续”任务等操作比较繁琐可能迫使你从新造轮子。微软也想到了所以在.NET4.0的时候加入了“并行任务”并在.NET4.5中对其进行改进想了解“并行任务”的园友可以先看看《译关于Async与Await的FAQ》。 本节到此结束感谢大家的观赏。赞的话还请多推荐啊 (*^_^*) 参考资料《CLR via C#(第三版)》
http://www.huolong8.cn/news/474178/

相关文章:

  • 咸宁有做网站的吗流量精灵官网
  • 北京seo网站诊断平台网站建设可行报告
  • 计算机网站建设与管理是什么意思哈尔滨市学府头道街52号
  • 设计很好看的网站申请关闭网站
  • 盐城网站开发公司杭州亚太建设监理咨询有限公司中标网站
  • 制造业外贸营销网站建设网站开发平台开发公司
  • 网站访客记录 是后台做吗ppt设计大赛
  • 关于做公司app的软件或网站asp在网站开发中的作用
  • 个人网站源码免费下载网页设计策划书ppt
  • 咖啡厅网站建设建网站找那家好
  • 高品质网站建设公司个人自己免费建网站
  • 温州网站优化指导怎么为自己公司做网站
  • 健康资讯网站模板南宁网站制作最新招聘信息
  • 顺企网杭州网站建设呼叫中心系统源码
  • crm网站下载做网站容易挣钱吗
  • 什么网站可以做相册视频宁波医院通网站建设
  • 响应式网站建设智能优化东阳网站建设怎么选
  • 用flash做网站超链接社保网站哪里做转入
  • 表格模板免费下载网站小米商城网站建设
  • 国内可以做网页的网站有啥可以自己做网站的软件
  • 德阳市建设局官方网站安全月网络规划与设计开题报告
  • 网站内容维护费用资讯网站的优势
  • 扬州网站建设电话本地门户网站系统
  • 陕西住房和城乡建设厅网站6贵阳市门户网站
  • 刘涛现在哪个网站做直播网站营销平台代理商
  • 车子网站网络营销渠道的类型有哪些
  • 一个云主机可以做多少网站大网站开发
  • 网站链接优化江苏苏州有什么好玩的
  • 展览展示设计必看网站做微电网的公司网站
  • 甘肃手机版建站系统价格移动服务器建设的电影网站