异步编程中的最佳做法(Async/Await) --转

近日来,涌现了许多关于 Microsoft .NET Framework 4.5 中新增了对 async 和 await 支持的信息。 本文旨在作为学习异步编程的“第二步”;我假设您已阅读过有关这一方面的至少一篇介绍性文章。 本文不提供任何新内容,Stack Overflow、MSDN 论坛和 async/await FAQ 这类在线资源提供了同样的建议。 本文只重点介绍一些淹没在文档海洋中的最佳做法。

本文中的最佳做法更大程度上是“指导原则”,而不是实际规则。 其中每个指导原则都有一些例外情况。 我将解释每个指导原则背后的原因,以便可以清楚地了解何时适用以及何时不适用。 图 1 中总结了这些指导原则;我将在以下各节中逐一讨论。

图 1 异步编程指导原则总结

“名称”说明异常
避免 Async Void最好使用 async Task 方法而不是 async void 方法事件处理程序
始终使用 Async不要混合阻塞式代码和异步代码控制台 main 方法
配置上下文尽可能使用 ConfigureAwait(false)需要上下文的方法

避免 Async Void

Async 方法有三种可能的返回类型: Task、Task<T> 和 void,但是 async 方法的固有返回类型只有 Task 和 Task<T>。 当从同步转换为异步代码时,任何返回类型 T 的方法都会成为返回 Task<T> 的 async 方法,任何返回 void 的方法都会成为返回 Task 的 async 方法。 下面的代码段演示了一个返回 void 的同步方法及其等效的异步方法:

  1.  
  2.           void MyMethod()
  3. {
  4.   // Do synchronous work.
  5.           Thread.Sleep(1000);
  6. }
  7. async Task MyMethodAsync()
  8. {
  9.   // Do asynchronous work.
  10.           await Task.Delay(1000);
  11. }
  12.         

返回 void 的 async 方法具有特定用途: 用于支持异步事件处理程序。 事件处理程序可以返回某些实际类型,但无法以相关语言正常工作;调用返回类型的事件处理程序非常困难,事件处理程序实际返回某些内容这一概念也没有太大意义。 事件处理程序本质上返回 void,因此 async 方法返回 void,以便可以使用异步事件处理程序。 但是,async void 方法的一些语义与 async Task 或 async Task<T> 方法的语义略有不同。

Async void 方法具有不同的错误处理语义。 当 async Task 或 async Task<T> 方法引发异常时,会捕获该异常并将其置于 Task 对象上。 对于 async void 方法,没有 Task 对象,因此 async void 方法引发的任何异常都会直接在 SynchronizationContext(在 async void 方法启动时处于活动状态)上引发。 图 2 演示本质上无法捕获从 async void 方法引发的异常。

图 2 无法使用 Catch 捕获来自 Async Void 方法的异常

  1.  
  2.           private async void ThrowExceptionAsync()
  3. {
  4.   throw new InvalidOperationException();
  5. }
  6. public void AsyncVoidExceptions_CannotBeCaughtByCatch()
  7. {
  8.   try
  9.   {
  10.     ThrowExceptionAsync();
  11.   }
  12.   catch (Exception)
  13.   {
  14.     // The exception is never caught here!
  15.           throw;
  16.   }
  17. }
  18.         

可以通过对 GUI/ASP.NET 应用程序使用 AppDomain.UnhandledException 或类似的全部捕获事件观察到这些异常,但是使用这些事件进行常规异常处理会导致无法维护。

Async void 方法具有不同的组合语义。 返回 Task 或 Task<T> 的 async 方法可以使用 await、Task.WhenAny、Task.WhenAll 等方便地组合而成。 返回 void 的 async 方法未提供一种简单方式,用于向调用代码通知它们已完成。 启动几个 async void 方法不难,但是确定它们何时结束却不易。 Async void 方法会在启动和结束时通知 SynchronizationContext,但是对于常规应用程序代码而言,自定义 SynchronizationContext 是一种复杂的解决方案。

Async void 方法难以测试。 由于错误处理和组合方面的差异,因此调用 async void 方法的单元测试不易编写。 MSTest 异步测试支持仅适用于返回 Task 或 Task<T> 的 async 方法。 可以安装 SynchronizationContext 来检测所有 async void 方法都已完成的时间并收集所有异常,不过只需使 async void 方法改为返回 Task,这会简单得多。

显然,async void 方法与 async Task 方法相比具有几个缺点,但是这些方法在一种特定情况下十分有用: 异步事件处理程序。 语义方面的差异对于异步事件处理程序十分有意义。 它们会直接在 SynchronizationContext 上引发异常,这类似于同步事件处理程序的行为方式。 同步事件处理程序通常是私有的,因此无法组合或直接测试。 我喜欢采用的一个方法是尽量减少异步事件处理程序中的代码(例如,让它等待包含实际逻辑的 async Task 方法)。 下面的代码演示了这一方法,该方法通过将 async void 方法用于事件处理程序而不牺牲可测试性:

  1.  
  2.           private async void button1_Click(object sender, EventArgs e)
  3. {
  4.   await Button1ClickAsync();
  5. }
  6. public async Task Button1ClickAsync()
  7. {
  8.   // Do asynchronous work.
  9.           await Task.Delay(1000);
  10. }
  11.         

如果调用方不希望 async void 方法是异步的,则这些方法可能会造成严重影响。 当返回类型是 Task 时,调用方知道它在处理将来的操作;当返回类型是 void 时,调用方可能假设方法在返回时完成。 此问题可能会以许多意外方式出现。 在接口(或基类)上提供返回 void 的方法的 async 实现(或重写)通常是错误的。某些事件也假设其处理程序在返回时完成。 一个不易察觉的陷阱是将 async lambda 传递到采用 Action 参数的方法;在这种情况下,async lambda 返回 void 并继承 async void 方法的所有问题。 一般而言,仅当 async lambda 转换为返回 Task 的委托类型(例如,Func<Task>)时,才应使用 async lambda。

总结这第一个指导原则便是,应首选 async Task 而不是 async void。 Async Task 方法更便于实现错误处理、可组合性和可测试性。 此指导原则的例外情况是异步事件处理程序,这类处理程序必须返回 void。 此例外情况包括逻辑上是事件处理程序的方法,即使它们字面上不是事件处理程序(例如 ICommand.Execute implementations)。

始终使用 Async

异步代码让我想起了一个故事,有个人提出世界是悬浮在太空中的,但是一个老妇人立即提出质疑,她声称世界位于一个巨大乌龟的背上。 当这个人问乌龟站在哪里时,老夫人回答:“很聪明,年轻人,下面是一连串的乌龟!”在将同步代码转换为异步代码时,您会发现,如果异步代码调用其他异步代码并且被其他异步代码所调用,则效果最好 — 一路向下(或者也可以说“向上”)。 其他人已注意到异步编程的传播行为,并将其称为“传染”或将其与僵尸病毒进行比较。 无论是乌龟还是僵尸,无可置疑的是,异步代码趋向于推动周围的代码也成为异步代码。 此行为是所有类型的异步编程中所固有的,而不仅仅是新 async/await 关键字。

“始终异步”表示,在未慎重考虑后果的情况下,不应混合使用同步和异步代码。 具体而言,通过调用 Task.Wait 或 Task.Result 在异步代码上进行阻塞通常很糟糕。 对于在异步编程方面“浅尝辄止”的程序员,这是个特别常见的问题,他们仅仅转换一小部分应用程序,并采用同步 API 包装它,以便代码更改与应用程序的其余部分隔离。 不幸的是,他们会遇到与死锁有关的问题。 在 MSDN 论坛、Stack Overflow 和电子邮件中回答了许多与异步相关的问题之后,我可以说,迄今为止,这是异步初学者在了解基础知识之后最常提问的问题: “为何我的部分异步代码死锁?”

图 3 演示一个简单示例,其中一个方法发生阻塞,等待 async 方法的结果。 此代码仅在控制台应用程序中工作良好,但是在从 GUI 或 ASP.NET 上下文调用时会死锁。 此行为可能会令人困惑,尤其是通过调试程序单步执行时,这意味着没完没了的等待。 在调用 Task.Wait 时,导致死锁的实际原因在调用堆栈中上移。

图 3 在异步代码上阻塞时的常见死锁问题

  1.  
  2.           public static class DeadlockDemo
  3. {
  4.   private static async Task DelayAsync()
  5.   {
  6.     await Task.Delay(1000);
  7.   }
  8.   // This method causes a deadlock when called in a GUI or ASP.NET context.
  9.           public static void Test()
  10.   {
  11.     // Start the delay.
  12.           var delayTask = DelayAsync();
  13.     // Wait for the delay to complete.
  14.           delayTask.Wait();
  15.   }
  16. }
  17.         

这种死锁的根本原因是 await 处理上下文的方式。 默认情况下,当等待未完成的 Task 时,会捕获当前“上下文”,在 Task 完成时使用该上下文恢复方法的执行。 此“上下文”是当前 SynchronizationContext(除非它是 null,这种情况下则为当前 TaskScheduler)。 GUI 和 ASP.NET 应用程序具有 SynchronizationContext,它每次仅允许一个代码区块运行。 当 await 完成时,它会尝试在捕获的上下文中执行 async 方法的剩余部分。但是该上下文已含有一个线程,该线程在(同步)等待 async 方法完成。 它们相互等待对方,从而导致死锁。

请注意,控制台应用程序不会形成这种死锁。 它们具有线程池 SynchronizationContext 而不是每次执行一个区块的 SynchronizationContext,因此当 await 完成时,它会在线程池线程上安排 async 方法的剩余部分。该方法能够完成,并完成其返回任务,因此不存在死锁。 当程序员编写测试控制台程序,观察到部分异步代码按预期方式工作,然后将相同代码移动到 GUI 或 ASP.NET 应用程序中会发生死锁,此行为差异可能会令人困惑。

此问题的最佳解决方案是允许异步代码通过基本代码自然扩展。 如果采用此解决方案,则会看到异步代码扩展到其入口点(通常是事件处理程序或控制器操作)。 控制台应用程序不能完全采用此解决方案,因为 Main 方法不能是 async。 如果 Main 方法是 async,则可能会在完成之前返回,从而导致程序结束。 图 4演示了指导原则的这一例外情况: 控制台应用程序的 Main 方法是代码可以在异步方法上阻塞为数不多的几种情况之一。

图 4 Main 方法可以调用 Task.Wait 或 Task.Result

  1.  
  2.           class Program
  3. {
  4.   static void Main()
  5.   {
  6.     MainAsync().Wait();
  7.   }
  8.   static async Task MainAsync()
  9.   {
  10.     try
  11.     {
  12.       // Asynchronous implementation.
  13.           await Task.Delay(1000);
  14.     }
  15.     catch (Exception ex)
  16.     {
  17.       // Handle exceptions.
  18.           }
  19.   }
  20. }
  21.         

允许异步代码通过基本代码扩展是最佳解决方案,但是这意味着需进行许多初始工作,该应用程序才能体现出异步代码的实际好处。 可通过几种方法逐渐将大量基本代码转换为异步代码,但是这超出了本文的范围。在某些情况下,使用 Task.Wait 或 Task.Result 可能有助于进行部分转换,但是需要了解死锁问题以及错误处理问题。 我现在说明错误处理问题,并在本文后面演示如何避免死锁问题。

每个 Task 都会存储一个异常列表。 等待 Task 时,会重新引发第一个异常,因此可以捕获特定异常类型(如 InvalidOperationException)。 但是,在 Task 上使用 Task.Wait 或 Task.Result 同步阻塞时,所有异常都会用 AggregateException 包装后引发。 请再次参阅图 4。 MainAsync 中的 try/catch 会捕获特定异常类型,但是如果将 try/catch 置于 Main 中,则它会始终捕获 AggregateException。 当没有 AggregateException 时,错误处理要容易处理得多,因此我将“全局”try/catch 置于 MainAsync 中。

至此,我演示了两个与异步代码上阻塞有关的问题: 可能的死锁和更复杂的错误处理。 对于在 async 方法中使用阻塞代码,也有一个问题。 请考虑此简单示例:

  1.  
  2.           public static class NotFullyAsynchronousDemo
  3. {
  4.   // This method synchronously blocks a thread.
  5.           public static async Task TestNotFullyAsync()
  6.   {
  7.     await Task.Yield();
  8.     Thread.Sleep(5000);
  9.   }
  10. }
  11.         

此方法不是完全异步的。 它会立即放弃,返回未完成的任务,但是当它恢复执行时,会同步阻塞线程正在运行的任何内容。 如果此方法是从 GUI 上下文调用,则它会阻塞 GUI 线程;如果是从 ASP.NET 请求上下文调用,则会阻塞当前 ASP.NET 请求线程。 如果异步代码不同步阻塞,则其工作效果最佳。 图 5 是将同步操作替换为异步替换的速查表。

图 5 执行操作的“异步方式”

执行以下操作…替换以下方式…使用以下方式
检索后台任务的结果Task.Wait 或 Task.Resultawait
等待任何任务完成Task.WaitAnyawait Task.WhenAny
检索多个任务的结果Task.WaitAllawait Task.WhenAll
等待一段时间Thread.Sleepawait Task.Delay

总结这第二个指导原则便是,应避免混合使用异步代码和阻塞代码。 混合异步代码和阻塞代码可能会导致死锁、更复杂的错误处理及上下文线程的意外阻塞。 此指导原则的例外情况是控制台应用程序的 Main 方法,或是(如果是高级用户)管理部分异步的基本代码。

配置上下文

在本文前面,我简要说明了当等待未完成 Task 时默认情况下如何捕获“上下文”,以及此捕获的上下文用于恢复 async 方法的执行。 图 3 中的示例演示在上下文上的恢复执行如何与同步阻塞发生冲突从而导致死锁。此上下文行为还可能会导致另一个问题 — 性能问题。 随着异步 GUI 应用程序在不断增长,可能会发现 async 方法的许多小部件都在使用 GUI 线程作为其上下文。 这可能会形成迟滞,因为会由于“成千上万的剪纸”而降低响应性。

若要缓解此问题,请尽可能等待 ConfigureAwait 的结果。 下面的代码段说明了默认上下文行为和 ConfigureAwait 的用法:

  1.  
  2.           async Task MyMethodAsync()
  3. {
  4.   // Code here runs in the original context.
  5.           await Task.Delay(1000);
  6.   // Code here runs in the original context.
  7.           await Task.Delay(1000).ConfigureAwait(
  8.     continueOnCapturedContext: false);
  9.   // Code here runs without the original
  10.   // context (in this case, on the thread pool).
  11.           }
  12.         

通过使用 ConfigureAwait,可以实现少量并行性: 某些异步代码可以与 GUI 线程并行运行,而不是不断塞入零碎的工作。

除了性能之外,ConfigureAwait 还具有另一个重要方面: 它可以避免死锁。 再次考虑图 3;如果向 DelayAsync 中的代码行添加“ConfigureAwait(false)”,则可避免死锁。 此时,当等待完成时,它会尝试在线程池上下文中执行 async 方法的剩余部分。 该方法能够完成,并完成其返回任务,因此不存在死锁。 如果需要逐渐将应用程序从同步转换为异步,则此方法会特别有用。

如果可以在方法中的某处使用 ConfigureAwait,则建议对该方法中此后的每个 await 都使用它。 前面曾提到,如果等待未完成的 Task,则会捕获上下文;如果 Task 已完成,则不会捕获上下文。 在不同硬件和网络情况下,某些任务的完成速度可能比预期速度更快,需要谨慎处理在等待之前完成的返回任务。 图 6 显示了一个修改后的示例。

图 6 处理在等待之前完成的返回任务

  1.  
  2.           async Task MyMethodAsync()
  3. {
  4.   // Code here runs in the original context.
  5.           await Task.FromResult(1);
  6.   // Code here runs in the original context.
  7.           await Task.FromResult(1).ConfigureAwait(continueOnCapturedContext: false);
  8.   // Code here runs in the original context.
  9.           var random = new Random();
  10.   int delay = random.Next(2); // Delay is either 0 or 1
  11.   await Task.Delay(delay).ConfigureAwait(continueOnCapturedContext: false);
  12.   // Code here might or might not run in the original context.
  13.           // The same is true when you await any Task
  14.   // that might complete very quickly.
  15.           }
  16.         

如果方法中在 await 之后具有需要上下文的代码,则不应使用 ConfigureAwait。 对于 GUI 应用程序,包括任何操作 GUI 元素、编写数据绑定属性或取决于特定于 GUI 的类型(如 Dispatcher/CoreDispatcher)的代码。 对于 ASP.NET 应用程序,这包括任何使用 HttpContext.Current 或构建 ASP.NET 响应的代码(包括控制器操作中的返回语句)。 图 7 演示 GUI 应用程序中的一个常见模式:让 async 事件处理程序在方法开始时禁用其控制,执行某些 await,然后在处理程序结束时重新启用其控制;因为这一点,事件处理程序不能放弃其上下文。

图 7 让 async 事件处理程序禁用并重新启用其控制

  1.  
  2.           private async void button1_Click(object sender, EventArgs e)
  3. {
  4.   button1.Enabled = false;
  5.   try
  6.   {
  7.     // Can't use ConfigureAwait here ...
  8.           await Task.Delay(1000);
  9.   }
  10.   finally
  11.   {
  12.     // Because we need the context here.
  13.           button1.Enabled = true;
  14.   }
  15. }
  16.         

每个 async 方法都具有自己的上下文,因此如果一个 async 方法调用另一个 async 方法,则其上下文是独立的。 图 8 演示的代码对图 7 进行了少量改动。

图 8 每个 async 方法都具有自己的上下文

  1.  
  2.           private async Task HandleClickAsync()
  3. {
  4.   // Can use ConfigureAwait here.
  5.           await Task.Delay(1000).ConfigureAwait(continueOnCapturedContext: false);
  6. }
  7. private async void button1_Click(object sender, EventArgs e)
  8. {
  9.   button1.Enabled = false;
  10.   try
  11.   {
  12.     // Can't use ConfigureAwait here.
  13.           await HandleClickAsync();
  14.   }
  15.   finally
  16.   {
  17.     // We are back on the original context for this method.
  18.           button1.Enabled = true;
  19.   }
  20. }
  21.         

无上下文的代码可重用性更高。 尝试在代码中隔离上下文相关代码与无上下文的代码,并尽可能减少上下文相关代码。 在图 8 中,建议将事件处理程序的所有核心逻辑都置于一个可测试且无上下文的 async Task 方法中,仅在上下文相关事件处理程序中保留最少量的代码。 即使是编写 ASP.NET 应用程序,如果存在一个可能与桌面应用程序共享的核心库,请考虑在库代码中使用 ConfigureAwait。

总结这第三个指导原则便是,应尽可能使用 Configure­Await。 无上下文的代码对于 GUI 应用程序具有最佳性能,是一种可在使用部分 async 基本代码时避免死锁的方法。 此指导原则的例外情况是需要上下文的方法。

了解您的工具

关于 async 和 await 有许多需要了解的内容,这自然会有点迷失方向。 图 9 是常见问题的解决方案的快速参考。

图 9 常见异步问题的解决方案

问题解决方案
创建任务以执行代码Task.Run 或 TaskFactory.StartNew(不是 Task 构造函数或 Task.Start)
为操作或事件创建任务包装TaskFactory.FromAsync 或 TaskCompletionSource<T>
支持取消CancellationTokenSource 和 CancellationToken
报告进度IProgress<T> 和 Progress<T>
处理数据流TPL 数据流或被动扩展
同步对共享资源的访问SemaphoreSlim
异步初始化资源AsyncLazy<T>
异步就绪生产者/使用者结构TPL 数据流或 AsyncCollection<T>

第一个问题是任务创建。 显然,async 方法可以创建任务,这是最简单的选项。 如果需要在线程池上运行代码,请使用 Task.Run。 如果要为现有异步操作或事件创建任务包装,请使用 TaskCompletionSource<T>。下一个常见问题是如何处理取消和进度报告。 基类库 (BCL) 包括专门用于解决这些问题的类型: CancellationTokenSource/CancellationToken 和 IProgress<T>/Progress<T>。 异步代码应使用基于任务的异步模式(或称为 TAP,msdn.microsoft.com/library/hh873175),该模式详细说明了任务创建、取消和进度报告。

出现的另一个问题是如何处理异步数据流。 任务很棒,但是只能返回一个对象并且只能完成一次。 对于异步流,可以使用 TPL 数据流或被动扩展 (Rx)。 TPL 数据流会创建类似于主角的“网格”。 Rx 更加强大和高效,不过也更加难以学习。 TPL 数据流和 Rx 都具有异步就绪方法,十分适用于异步代码。

仅仅因为代码是异步的,并不意味着就安全。 共享资源仍需要受到保护,由于无法在锁中等待,因此这比较复杂。 下面是一个异步代码示例,该代码如果执行两次,则可能会破坏共享状态,即使始终在同一个线程上运行也是如此:

int value;

Task<int> GetNextValueAsync(int current);

async Task UpdateValueAsync()

{

  value = await GetNextValueAsync(value);

}

  1.  
  2.         

问题在于,方法读取值并在等待时挂起自己,当方法恢复执行时,它假设值未更改。 为了解决此问题,使用异步就绪 WaitAsync 重载扩展了 SemaphoreSlim 类。 图 10 演示 SemaphoreSlim.WaitAsync。

图 10 SemaphoreSlim 允许异步同步

SemaphoreSlim mutex = new SemaphoreSlim(1);

int value;

Task<int> GetNextValueAsync(int current);

async Task UpdateValueAsync()

{

  await mutex.WaitAsync().ConfigureAwait(false);

  try

  {

    value = await GetNextValueAsync(value);

  }

  finally

  {

    mutex.Release();

  }

}

  1.  
  2.         

异步代码通常用于初始化随后会缓存并共享的资源。 没有用于此用途的内置类型,但是 Stephen Toub 开发了 AsyncLazy<T>,其行为相当于 Task<T> 和 Lazy<T> 合二为一。 该原始类型在其博客 (bit.ly/dEN178) 上进行了介绍,并且在我的 AsyncEx 库 (nitoasyncex.codeplex.com) 中提供了更新版本。

最后,有时需要某些异步就绪数据结构。 TPL 数据流提供了 BufferBlock<T>,其行为如同异步就绪生产者/使用者队列。 而 AsyncEx 提供了 AsyncCollection<T>,这是异步版本的 BlockingCollection<T>。

我希望本文中的指导原则和指示能有所帮助。 异步真的是非常棒的语言功能,现在正是开始使用它的好时机!

转载于:https://www.cnblogs.com/Zhaowh/p/3904018.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/432387.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

spline 用法

控制actor在level中沿着一个spline path运动。 由finterp to 函数的输出数值来控制每一帧actor运动的距离。 从开始位置到spline path的终点的时间&#xff0c;是1/interp speed。假如我们控制actor从开头到终点运动的总时间是1秒&#xff0c;那么 interp speed就传入0.5&#…

php 时间戳获取周几,PHP实现根据时间戳获取周几的方法,php戳获取周_PHP教程

PHP实现根据时间戳获取周几的方法&#xff0c;php戳获取周本文实例讲述了PHP实现根据时间戳获取周几的方法。分享给大家供大家参考&#xff0c;具体如下&#xff1a;获取某个时间戳的周几&#xff0c;以及未来几天以后的周几其中&#xff1a;$time 代表时间$i 今天开始具体示例…

HTML5 Canvas 高仿逼真 3D 布料图案效果

HTML5 规范引进了很多新特性&#xff0c;其中最令人期待的之一就是 Canvas 元素&#xff0c;HTML5 Canvas 提供了通过 JavaScript 绘制图形的方法&#xff0c;非常强大。下面给大家分享一个 HTML5 Canvas 绘制的 3D 布料图案效果。 温馨提示&#xff1a;为保证最佳的效果&#…

oracle中exp/imp讲解

Exp/Imp是oracle备份数据的两个命令行工具 1.本地数据库导入导出 1.导出 &#xff08;运行---cmd中操作&#xff09; exp 用户名/密码数据库实例名file本地存放路径 eg: exp jnjp/jnjpORCL fileC:/jnjp.dmp2.导入 imp 用户名/密码数据库实例名file本地数据库文件存放路径fully …

用toad实现oracle数据迁移,Oracle 使用TOAD实现导入导出Excel数据

在Oracle应用程序的开发过程中&#xff0c;访问数据库对象和编写SQL程序是一件乏味且耗费时间的工作&#xff0c;对数据库进行日常管理也是需要很多SQL脚本才能完成的。Quest Software为此提供了高效的Oracle应用开发工具-Toad。在Toad的新版本中&#xff0c;还加入了DBA模块&a…

本地windows主机无法访问虚拟机里主机解决办法

一&#xff1a;设置虚拟机里IP&#xff0c;使其与本地计算机IP在同一网段 本地计算机网络IP设置如下&#xff1a; 虚拟机里ip为192.168.1.9 设置IP步骤请参考&#xff1a;Linux里如何设置IP(RED HAT) 二&#xff1a;将虚拟机网络连接方式设为桥接 假如&#xff0c;VirtualBox不…

Oracle创建用户、角色、授权、建表

oracle数据库的权限系统分为系统权限与对象权限。系统权限( database system privilege )可以让用户执行特定的命令集。例如&#xff0c;create table权限允许用户创建表&#xff0c;grant any privilege 权限允许用户授予任何系统权限。对象权限( database object privilege )…

linux服务器3306端口,linux系统对外开放3306、8080等端口,防火墙设置详解

linux系统对外开放3306、8080等端口&#xff0c;防火墙设置详解发布时间&#xff1a;2020-10-10 23:08:49来源&#xff1a;脚本之家阅读&#xff1a;141作者&#xff1a;julielele栏目&#xff1a;服务器我们很多时候在liunx系统上安装了web服务应用后(如tomcat、apache等)&…

hdu--1231--并查集连分量的个数

我觉得 这题 是纯粹的 并查集 可以算成 入门题吧 问你有几章桌子 就是问你有几个 连通块嘛 一个道理 touch me 这题 我采用了下 father[x]开始 初始化为-1 1 #include <iostream>2 #include <cstring>3 using namespace std;4 5 const int size 1010;6 int fathe…

linux查询服务器的dns,如何查看Linux系统中DNS服务器的运行状况

DNS是分布式数据库&#xff0c;可以让用户们方便访问互联网&#xff0c;而主机的dns服务器也直接影响了我们上网及访问网站的速度&#xff0c;那么你知道如何查看Linux系统中DNS服务器的运行状况吗&#xff1f;在Linux环境下&#xff0c;也提供了广泛流行的BIND服务器&#xff…

landscape 1

1. 创建出landscape的分层材质2. 把材质添加给landscape后&#xff0c;可以在landscape的画刷模式下看到3. 通过点击右侧的加号&#xff0c;依次添加layer后&#xff0c;就可以使用他们进行地形的绘制。 通过右键菜单可以快速填充和清除对应的layer4. 用下面的方法修改材质的法…

linux进程和线程教程,Linux下查看进程和线程

在Linux中查看线程数的三种方法1、top -H手册中说&#xff1a;-H : Threads toggle加上这个选项启动top&#xff0c;top一行显示一个线程。否则&#xff0c;它一行显示一个进程。2、ps xH手册中说&#xff1a;H Show threads as if they were processes这样可以查看所有存在的线…

ORA-12154: TNS: 无法解析指定的连接标识符

相信使用过Oracle数据库的人一定碰到过“ORA-12154: TNS: 无法解析指定的连接标识符”错误&#xff0c;我在此做一个小小的总结。在程序中连接Oracle数据库的方式与其他常用数据库&#xff0c;如&#xff1a;MySql,Sql Server不同&#xff0c;这些数据库可以通过直接指定IP的方…

任务管理器启动资源管理器

今天远程桌面登录windows服务器竟然发现没有Windows Explorer&#xff08;资源管理器&#xff09;了&#xff01;桌面啥的都没有&#xff0c;只有几个孤零零的命令行窗口。。。 输入ctrlaltend启动远程桌面的任务管理器&#xff0c;选择File->run new task 输入explorer.exe…

linux无后缀名程序运行,linux – 如何在Ubuntu上运行无扩展(也许是ELF)文件?

我的猜测是这是64位系统上的32位编译.我使用gcc上的-m32选项将一个小的c文件交叉编译成二进制文件.这还需要一些额外的包.结果a.out看起来像这样.% file a.outa.out: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linu…

oracle 11g exp 报错 EXP-00056;ORA-12154;EXP-00000;

在Oracle学习过程中&#xff0c;使用exp导出数据&#xff0c;使用如下命令&#xff1a;[plain] view plaincopyexp username/passworddatabase_name filed:\filename.dmp buffer1048000 tables(tablename) query\"where key in (value1)\"; 出现错误提示&#xff1…

mysql设置

设置mysql不区分大小写 http://blog.163.com/pursue100yeah/blog/static/165732158201101641221182/ 在/etc/my.cnf 中的[mysqld]后添加添加lower_case_table_names1&#xff0c;重启MYSQL服务&#xff0c;这时已设置成功&#xff1a;不区分表名的大小写&#xff1b; 转载于:ht…

双网卡绑定linux7.2,CentOS 7.2 bond实现网卡聚合链路(双网卡绑定)脚本及验证(适合云平台)...

使用bond实现网卡聚合链路脚本:#!/bin/bash#创建一个名为bond0的链路接口IP192.168.101.1GATE192.168.101.254ETH1eno1ETH2eno2ETH3eno3ETH4eno4modprobebondingcat</etc/sysconfig/network-scripts/ifcfg-bond0DEVICEbond0TYPEBondNAMEbond0BONDING_MASTERyesBOOTPROTOstat…

Oracle的Net Configuration Assistant 配置

在进行团队开发的时候&#xff0c;一般团队的每一个人只需要安装一个客户端即可&#xff0c;没有必要安装一个Oracle 数据库服务器&#xff0c;而数据库服务器是属于共享的&#xff0c;此时&#xff0c;我们就需要配置客户端。客户端的配置可以有以下两种方式&#xff1a;第一种…

windows下hadoop的单机伪分布式部署(3)

下面介绍myeclipse与hadoop的集成。 我用的myeclipse版本是8.5. 1、安装hadoop开发插件 在hadoop1.2.1版本的安装包contrib/目录下&#xff0c;已经不再提供hadoop-eclipse-pligin-1.2.1.jar; 而是提供了源代码文件&#xff0c;需要我们自行重新编译成jar包文件&#xff1b;这里…