.NET Core 多线程的用法,以及用例
1.使用 Thread 类
Thread 类是 .NET 中最基本的多线程操作方式之一,可以使用它创建并启动新线程。以下是一个简单的例子,创建一个新的线程并运行:
using System;
using System.Threading;
class Program
{
static void Main()
{
Thread t = new Thread(new ThreadStart(ThreadProc));
t.Start();
// 等待线程执行结束
t.Join();
Console.WriteLine("Main thread exiting.");
}
static void ThreadProc()
{
Console.WriteLine("ThreadProc starting...");
Thread.Sleep(1000);
Console.WriteLine("ThreadProc ending.");
}
}
2.使用 Task 类
Task 类是 .NET 中推荐使用的多线程操作方式之一,它可以更方便地管理异步操作和多个任务。以下是一个简单的例子,创建一个新的 Task 并启动:
using System;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
await Task.Run(() =>
{
Console.WriteLine("Task starting...");
Thread.Sleep(1000);
Console.WriteLine("Task ending.");
});
Console.WriteLine("Main thread exiting.");
}
}
3.使用 Parallel 类
Parallel 类可以让我们更方便地进行并行化操作,它提供了一系列方法,可以将一个任务分割成多个小任务,并让多个线程同时执行这些小任务。以下是一个简单的例子:
using System;
using System.Threading.Tasks;
class Program
{
static void Main()
{
Parallel.For(0, 10, i =>
{
Console.WriteLine("Task {0} starting...", i);
Thread.Sleep(1000);
Console.WriteLine("Task {0} ending.", i);
});
Console.WriteLine("Main thread exiting.");
}
}
4.使用 async/await
在 .NET Core 中,可以使用 async/await 关键字进行异步操作,这是一种非常方便的操作多线程的方式。async/await 让代码看起来像是同步的,但实际上是在后台使用多线程异步执行的。以下是一个简单的例子:
using System;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
Console.WriteLine("Main thread starting...");
await Task.Run(() =>
{
Console.WriteLine("Task starting...");
Thread.Sleep(1000);
Console.WriteLine("Task ending.");
});
Console.WriteLine("Main thread exiting.");
}
}
5.使用 Concurrent 类
Concurrent 类提供了线程安全的集合和队列,它们可以在多个线程中同时访问和修改,而不会发生冲突和数据损坏。以下是一个简单的例子,使用 ConcurrentQueue 存储数据:
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
class Program
{
static void Main()
{
ConcurrentQueue<int> queue = new ConcurrentQueue<int>();
// 并行化生产数据
Parallel.For(0, 10, i =>
{
Console.WriteLine("Task {0} producing...", i);
queue.Enqueue(i);
});
// 并行化消费数据
Parallel.For(0, 10, i =>
{
int value;
if (queue.TryDequeue(out value))
{
Console.WriteLine("Task {0} consuming {1}...", i, value);
}
else
{
Console.WriteLine("Task {0} found queue empty...", i);
}
});
Console.WriteLine("Main thread exiting.");
}
}
以下是一些高级和复杂一些的操作多线程的用法和技巧:
1.使用 Lock 和 Monitor
在多线程中,如果多个线程同时访问和修改共享资源,会导致数据损坏和程序崩溃。为了避免这种情况,可以使用 lock 和 Monitor 关键字进行同步。lock 和 Monitor 用于获取对象的锁,并保证在同一时间只有一个线程可以访问该对象。以下是一个简单的例子:
using System;
using System.Threading;
class Program
{
static object _lock = new object();
static int _counter = 0;
static void Main()
{
Thread t1 = new Thread(IncrementCounter);
Thread t2 = new Thread(IncrementCounter);
t1.Start();
t2.Start();
t1.Join();
t2.Join();
Console.WriteLine("Counter = {0}", _counter);
}
static void IncrementCounter()
{
for (int i = 0; i < 100000; i++)
{
lock (_lock)
{
_counter++;
}
}
}
}
2.使用 CancellationToken 和 TaskCompletionSource
在异步操作中,有时需要取消任务或等待任务完成后执行其他操作。为了实现这些功能,可以使用 CancellationToken 和 TaskCompletionSource 类。CancellationToken 用于取消任务,TaskCompletionSource 用于等待任务完成并返回结果。以下是一个简单的例子:
using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
using (CancellationTokenSource cts = new CancellationTokenSource())
{
Task<int> task = DoWorkAsync(cts.Token);
// 等待任务完成或取消
Task completedTask = await Task.WhenAny(task, Task.Delay(5000));
if (completedTask == task)
{
Console.WriteLine("Result = {0}", await task);
}
else
{
Console.WriteLine("Task cancelled.");
cts.Cancel();
}
}
}
static async Task<int> DoWorkAsync(CancellationToken cancellationToken)
{
try
{
await Task.Delay(2000, cancellationToken);
return 42;
}
catch (OperationCanceledException)
{
Console.WriteLine("DoWorkAsync cancelled.");
throw;
}
}
}
3.使用 ThreadLocal 和 ExecutionContext
在某些情况下,需要在多个线程中共享变量,并且每个线程需要使用不同的值。为了实现这个目标,可以使用 ThreadLocal 类。ThreadLocal 类为每个线程提供一个独立的变量副本,使得每个线程都可以使用不同的值。以下是一个简单的例子:
using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static ThreadLocal<int> _counter = new ThreadLocal<int>(() => 0);
static void Main()
{
Parallel.For(0, 10, i =>
{
_counter.Value++;
Console.WriteLine("Thread {0} counter = {1}", Thread.CurrentThread.ManagedThreadId, _counter.Value);
});
Console.WriteLine("Main thread exiting.");
}
}
ExecutionContext 类可以用于在多个线程中共享数据
4.使用 Parallel 类和 PLINQ
Parallel 类和 PLINQ(Parallel LINQ)是 .NET Framework 中用于并行处理数据的工具。Parallel 类提供了一些方法,如 For 和 ForEach,可以轻松地将循环并行化。PLINQ 则是 LINQ 的并行版本,它可以将查询操作并行化。以下是一个简单的例子:
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static void Main()
{
int[] numbers = Enumerable.Range(0, 1000000).ToArray();
// 并行循环
Parallel.ForEach(numbers, number =>
{
Console.WriteLine("Thread {0} processing number {1}.", Thread.CurrentThread.ManagedThreadId, number);
});
// 并行查询
var result = numbers.AsParallel().Where(number => number % 2 == 0).Sum();
Console.WriteLine("Result = {0}", result);
}
}
5.使用 SemaphoreSlim 和 CountdownEvent
SemaphoreSlim 和 CountdownEvent 是用于控制多个线程之间的同步和协作的类。SemaphoreSlim 可以用于限制同时访问某些资源的线程数量,CountdownEvent 可以用于在所有线程完成某些操作后恢复执行。以下是一个简单的例子:
using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static SemaphoreSlim _semaphore = new SemaphoreSlim(2);
static CountdownEvent _countdown = new CountdownEvent(2);
static void Main()
{
Task t1 = Task.Run(() => DoWork(1));
Task t2 = Task.Run(() => DoWork(2));
Task t3 = Task.Run(() => DoWork(3));
Task t4 = Task.Run(() => DoWork(4));
Task.WaitAll(t1, t2, t3, t4);
Console.WriteLine("Main thread exiting.");
}
static void DoWork(int id)
{
_semaphore.Wait();
try
{
Console.WriteLine("Thread {0} working.", id);
Thread.Sleep(2000);
}
finally
{
_semaphore.Release();
_countdown.Signal();
}
}
}
在这个例子中,SemaphoreSlim 限制了同时执行的线程数量,CountdownEvent 则用于在所有线程完成后恢复执行
6.使用 TaskCompletionSource 和 async/await
TaskCompletionSource 可以用于将异步操作转换为 Task 对象,这使得异步操作可以与同步代码一样进行操作。async/await 则是 .NET Framework 4.5 中引入的关键字,它可以将异步代码看作同步代码,使得异步编程更加简单和直观。以下是一个简单的例子
using System;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
Console.WriteLine("Main thread started.");
Task<int> task = DoWorkAsync();
int result = await task;
Console.WriteLine("Result = {0}.", result);
Console.WriteLine("Main thread exiting.");
}
static async Task<int> DoWorkAsync()
{
Console.WriteLine("Worker thread started.");
await Task.Delay(2000);
Console.WriteLine("Worker thread completed.");
return 42;
}
}
在这个例子中,DoWorkAsync 方法使用 async/await 异步地执行工作,并返回一个 Task<int> 对象。Main 方法则使用 await 等待 DoWorkAsync 方法的执行,并获取返回值。这使得异步编程更加简单和直观
7.使用 Dataflow
Dataflow 是 .NET Framework 4.5 中引入的一种并发编程模型,它可以用于建立数据流管道,将多个数据处理步骤连接起来,形成一个完整的数据处理流程。Dataflow 可以处理包括异步和同步操作在内的各种数据处理任务。以下是一个简单的例子
using System;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
class Program
{
static async Task Main()
{
Console.WriteLine("Main thread started.");
// 创建数据流管道
var pipeline = new TransformBlock<int, int>(async x =>
{
Console.WriteLine("Thread {0} processing number {1}.", Thread.CurrentThread.ManagedThreadId, x);
await Task.Delay(1000);
return x * 2;
});
// 将多个数据处理步骤连接起来
pipeline.LinkTo(new ActionBlock<int>(x =>
{
Console.WriteLine("Thread {0} processed number {1}.", Thread.CurrentThread.ManagedThreadId, x);
}));
// 将数据发送到管道中
for (int i = 0; i < 10; i++)
{
pipeline.Post(i);
}
// 等待管道处理完成
pipeline.Complete();
await pipeline.Completion;
Console.WriteLine("Main thread exiting.");
}
}
在这个例子中,使用 TransformBlock 和 ActionBlock 创建了一个数据流管道,将多个数据处理步骤连接起来。在管道中发送数据时,每个数据处理步骤会异步地处理数据,并将处理结果传递给下一个数据处理步骤。使用 Dataflow 可以更加方便地处理复杂的数据处理任务。
8.使用 Parallel 和 PLINQ
Parallel 和 PLINQ 是 .NET Framework 中提供的两种并发编程模型,它们都可以用于并行执行多个操作,提高代码的性能和并发度。以下是一个简单的例子:
using System;
using System.Linq;
using System.Threading.Tasks;
class Program
{
static void Main()
{
Console.WriteLine("Main thread started.");
int[] numbers = Enumerable.Range(1, 10).ToArray();
// 使用 Parallel.For 并行处理数据
Parallel.For(0, numbers.Length, i =>
{
Console.WriteLine("Thread {0} processing number {1}.", Thread.CurrentThread.ManagedThreadId, numbers[i]);
Thread.Sleep(1000);
});
// 使用 PLINQ 并行处理数据
var results = numbers.AsParallel().Select(x =>
{
Console.WriteLine("Thread {0} processing number {1}.", Thread.CurrentThread.ManagedThreadId, x);
Thread.Sleep(1000);
return x * 2;
}).ToList();
Console.WriteLine("Results: {0}.", string.Join(", ", results));
Console.WriteLine("Main thread exiting.");
}
}
在这个例子中,使用 Parallel.For 和 PLINQ 并行处理了一个数组中的数据。Parallel.For 使用指定的起始和结束索引并行执行多个操作,而 PLINQ 使用 AsParallel 方法将数据集合并行化,并在多个线程上执行 LINQ 操作。使用 Parallel 和 PLINQ 可以更加方便地提高代码的性能和并发度。
9.使用 ThreadLocal
ThreadLocal 是 .NET Framework 中提供的一种线程局部存储机制,它可以让每个线程拥有自己独立的数据副本,避免了线程之间的竞争和同步问题。以下是一个简单的例子
using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static ThreadLocal<int> _count = new ThreadLocal<int>(() => 0);
static void Main()
{
Console.WriteLine("Main thread started.");
// 创建多个线程并执行任务
var tasks = new Task[3];
for (int i = 0; i < 3; i++)
{
tasks[i] = Task.Factory.StartNew(() =>
{
_count.Value++;
Console.WriteLine("Thread {0} count = {1}.", Thread.CurrentThread.ManagedThreadId, _count.Value);
Thread.Sleep(1000);
_count.Value--;
});
}
Task.WaitAll(tasks);
Console.WriteLine("Main thread exiting.");
}
}
在这个例子中,使用 ThreadLocal 创建了一个线程局部变量,每个线程都拥有自己独立的数据副本。在多个线程执行任务时,可以使用 _count.Value 获取每个线程独立的数据副本。使用 ThreadLocal 可以避免线程之间的竞争和同步问题。
10.使用 SemaphoreSlim
SemaphoreSlim 是 .NET Framework 中提供的一种轻量级信号量机制,它可以用于控制并发度和资源访问。以下是一个简单的例子:
using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static SemaphoreSlim _semaphore = new SemaphoreSlim(2);
static void Main()
{
Console.WriteLine("Main thread started.");
// 创建多个线程并执行任务
var tasks = new Task[5];
for (int i = 0; i < 5; i++)
{
tasks[i] = Task.Factory.StartNew(async () =>
{
Console.WriteLine("Thread {0} waiting for semaphore.", Thread.CurrentThread.ManagedThreadId);
// 等待信号量
await _semaphore.WaitAsync();
try
{
Console.WriteLine("Thread {0} acquired semaphore.", Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(1000);
}
finally
{
// 释放信号量
_semaphore.Release();
Console.WriteLine("Thread {0} released semaphore.", Thread.CurrentThread.ManagedThreadId);
}
});
}
Task.WaitAll(tasks);
Console.WriteLine("Main thread exiting.");
}
}
在这个例子中,使用 SemaphoreSlim 创建了一个信号量,它的初始计数为 2,表示最多有两个线程同时访问。在多个线程执行任务时,可以使用 _semaphore.WaitAsync() 获取信号量并等待资源,使用 _semaphore.Release() 释放信号量。使用 SemaphoreSlim 可以控制并发度和资源访问,避免资源竞争和死锁问题。
总结:
.NET Core 中提供了丰富的多线程编程模型和工具,包括 Task、async/await、ThreadPool、Thread、Concurrent、Parallel、PLINQ、ThreadLocal 和 SemaphoreSlim 等。这些工具可以帮助我们更加方便地实现并发编程,提高代码的性能和并发度。在使用多线程编程时,我们需要注意避免常见的线程安全问题,例如资源竞争、死锁和数据不一致等。同时,我们还可以使用一些工具和技术来帮助我们发现和解决线程安全问题,例如代码审查、单元测试和性能分析等。
解决线程安全问题是多线程编程中非常重要的一环。下面我将介绍几种解决线程安全问题的方法:
1.使用锁机制:锁机制可以确保在同一时刻只有一个线程能够访问共享资源。可以使用 lock 关键字或 Monitor 类来实现锁机制。
2.使用互斥量:互斥量也可以用来控制对共享资源的访问。与锁机制不同的是,互斥量可以跨进程使用。
3.使用信号量:信号量可以用来限制对共享资源的访问。它可以控制同时访问共享资源的线程数量。
4.使用原子操作:原子操作是一种特殊的操作,它能够确保在执行操作期间没有其他线程能够访问同一共享资源。
5.使用并发集合:并发集合是一种特殊的数据结构,它们专门设计用来在多线程环境下安全地访问共享资源。.NET Core 提供了许多种并发集合,例如 ConcurrentDictionary、ConcurrentQueue、ConcurrentBag 等。
6.尽量避免共享资源:如果可能的话,可以尝试避免共享资源的使用。这样可以减少线程之间的竞争和冲突。
7.使用线程安全的类型:在编写多线程代码时,可以使用线程安全的类型,例如 Interlocked、Volatile 和 ThreadLocal 等。
总之,在解决线程安全问题时,需要注意避免死锁、饥饿、活锁等问题,并在编写代码时仔细考虑多线程访问的顺序、数据的同步和共享资源的保护。同时,进行代码审查、单元测试和性能分析等,可以帮助我们发现和解决线程安全问题。
下面是几个具体的示例,演示如何解决常见的线程安全问题。
1.使用锁机制
class BankAccount
{
private object accountLock = new object();
private decimal balance;
public void Deposit(decimal amount)
{
lock (accountLock)
{
balance += amount;
}
}
public void Withdraw(decimal amount)
{
lock (accountLock)
{
balance -= amount;
}
}
}
在这个示例中,使用 lock 关键字来确保在 Deposit 和 Withdraw 方法执行期间,同一时刻只有一个线程能够访问 balance 变量。
2.使用并发集合
ConcurrentDictionary<string, int> dict = new ConcurrentDictionary<string, int>();
dict.TryAdd("one", 1);
dict.TryAdd("two", 2);
dict.TryAdd("three", 3);
foreach (var item in dict)
{
Console.WriteLine($"{item.Key}: {item.Value}");
}
在这个示例中,使用 ConcurrentDictionary 类来存储键值对。ConcurrentDictionary 是线程安全的,多个线程可以同时访问它而不会产生竞争和冲突。
3.使用互斥量
class MyMutex
{
private Mutex mutex = new Mutex();
private int count = 0;
public void AddCount()
{
mutex.WaitOne();
try
{
count++;
}
finally
{
mutex.ReleaseMutex();
}
}
public int GetCount()
{
mutex.WaitOne();
try
{
return count;
}
finally
{
mutex.ReleaseMutex();
}
}
}
在这个示例中,使用 Mutex 类来实现互斥量。在 AddCount 和 GetCount 方法执行期间,同一时刻只有一个线程能够访问 count 变量。
总之,以上示例演示了如何使用不同的技术解决线程安全问题。需要根据具体情况选择最适合的方法,同时注意代码的效率和性能。
4.避免死锁
1.下面是一个可能导致死锁的示例:
class DeadlockExample
{
private object lockA = new object();
private object lockB = new object();
public void MethodA()
{
lock (lockA)
{
Console.WriteLine("MethodA acquired lockA.");
lock (lockB)
{
Console.WriteLine("MethodA acquired lockB.");
}
}
}
public void MethodB()
{
lock (lockB)
{
Console.WriteLine("MethodB acquired lockB.");
lock (lockA)
{
Console.WriteLine("MethodB acquired lockA.");
}
}
}
}
这个示例中,两个方法 MethodA 和 MethodB 都需要获取两个锁 lockA 和 lockB。如果两个方法在不同的线程上同时执行,那么可能会发生死锁,导致两个线程互相等待对方释放锁,最终导致程序停滞不前。
为了避免死锁,可以改变锁的获取顺序。例如,在上面的示例中,可以将 MethodB 中获取锁的顺序改为 lockA, lockB,这样就避免了死锁的问题。
2.使用线程安全的数据结构
如果您需要在多个线程之间共享数据,可以使用线程安全的数据结构,例如 BlockingCollection、ConcurrentQueue 和 ConcurrentStack。这些数据结构都是线程安全的,可以避免多个线程同时访问同一个变量的问题。
例如,下面的示例演示了如何使用 BlockingCollection 来实现生产者-消费者模式:
class ProducerConsumerExample
{
private BlockingCollection<int> queue = new BlockingCollection<int>(10);
public void Produce()
{
for (int i = 0; i < 20; i++)
{
queue.Add(i);
}
queue.CompleteAdding();
}
public void Consume()
{
foreach (var item in queue.GetConsumingEnumerable())
{
Console.WriteLine(item);
}
}
}
在这个示例中,一个生产者线程使用 Add 方法向队列中添加数据,一个消费者线程使用 GetConsumingEnumerable 方法获取数据。由于 BlockingCollection 是线程安全的,因此不需要担心数据访问的竞争和冲突问题。
总之,线程安全是一个非常重要的问题,需要特别注意。使用适当的技术和方法,可以避免大多数线程安全问题。
.NET Core 多线程的用法,以及用例的更多相关文章
- C#多线程的用法8-线程间的协作AutoResetEvent
AutoResetEvent自动重置事件,与ManualResetEvent是相对的而言.它同样用于线程间同步,请对照<C#多线程的用法7-线程间的协作ManualResetEvent>进 ...
- Python爬虫进阶五之多线程的用法
前言 我们之前写的爬虫都是单个线程的?这怎么够?一旦一个地方卡到不动了,那不就永远等待下去了?为此我们可以使用多线程或者多进程来处理. 首先声明一点! 多线程和多进程是不一样的!一个是 thread ...
- 30个php操作redis经常用法代码样例
这篇文章主要介绍了30个php操作redis经常用法代码样例,本文事实上不止30个方法,能够操作string类型.list类型和set类型的数据,须要的朋友能够參考下 redis的操作非常多的,曾经看 ...
- ASP.NET Core 中间件基本用法
ASP.NET Core 中间件 ASP.NET Core的处理流程是一个管道,而中间件是装配到管道中的用于处理请求和响应的组件.中间件按照装配的先后顺序执行,并决定是否进入下一个组件.中间件管道的处 ...
- C#多线程的用法10-线程池
TheadPool:在进行多线程编程时,如果不想频繁的创建线程,那可以考虑使用使用线程池来完成多线程编程的工作.你只需将要处理的任务交付给ThreadPool,如果ThreadPool中有空闲的线程, ...
- Java线程和多线程(五)——单例类中的线程安全
单例模式是最广泛使用的创建模式之一.在现实世界之中,诸如Databae的连接或者是企业信息系统(EIS)等,通常其创建都是受到限制的,应该尽量复用已存在对象而不是频繁创建销毁.为了达到这个目的,开发者 ...
- python 多线程中同步的小样例
#!/usr/bin/python # -*- coding: UTF-8 -*- # 在一个资源池中.获取资源 # Author: zhang # Date: 2015-7-27 import ti ...
- pytest(13)-多线程、多进程执行用例
有些项目的测试用例较多,测试用例时需要分布式执行,缩短运行时间. pytest框架中提供可用于分布式执行测试用例的插件:pytest-parallel.pytest-xdist,接下来我们来学习这两个 ...
- C#多线程的用法5-线程间的协作Monitor
之前我们使用lock快捷方式,实现了多线程对同一资源的共享.在C#中lock实际上是Monitor操作的简化版本. 下面使用Monitor来完成之前的lock功能,你可以在此做一下对照: privat ...
- C#多线程的用法4-线程间的协作lock快捷方式
线程间协作还可通过lock(加锁)方式进行,lock属于C#的Monitor语法糖(Monitor后续讲解).使用简单快捷,如下: /// <summary> /// 多线程协作-lock ...
随机推荐
- java打印杨辉三角
package com.dylan.practice.interview; /** * 打印杨辉三角 * * 原理 * 1.每个数等于它上方两数之和 * 2.第n行的数字有n个 * * @author ...
- 《系列二》-- 5、单例bean缓存的获取
目录 1 判断bean是否完成整个加载流程 2 判断当前bean是否被加载过,是否已作为提前暴露的bean 关于循环依赖 阅读之前要注意的东西:本文就是主打流水账式的源码阅读,主导的是一个参考,主要内 ...
- 如何用Apipost校验响应结果
数据校验的意义 我们可以通过 json-schema 预先定义接口的数据返回格式,当接口完成后,我们可以通过匹配 实际响应结果 和 预先定义的接口格式 ,来发现接口问题.如下图: 数据校验的设置 我们 ...
- 教你如何判断Java代码中异步操作是否完成
本文分享自华为云社区<java代码实现异步返回结果如何判断异步执行完成>,作者: 皮牙子抓饭. 在许多应用程序中,我们经常使用异步操作来提高性能和响应度.在Java中,我们可以使用多线程或 ...
- RabbitMQ和RPC
消息队列 消息队列中间件 (Message Queue Middleware,简称 MQ) 是指利用高效可靠的消息传递机制进行与平台无关的数据交流,它可以在分布式环境下扩展进程间的数据通信,并基于数据 ...
- .NET Core 引发的异常:“sqlsugar.sqlsugarexception” 位于 system.private.corelib.dll 中
运行一个.NET Core 项目 报错:引发的异常:"sqlsugar.sqlsugarexception" 位于 system.private.corelib.dll 中 . 我 ...
- 02、etcd单机部署和集群部署
上一章我们认识了etcd,简单的介绍了 etcd 的基础概念,但是理解起来还是比较抽象的.这一章我们就一起来部署下 etcd .这样可以让我们对 etcd 有更加确切的认识. 1.etcd单实例部署 ...
- 扣子(coze.cn)| 由浅入深,手把手带你实现Java转型学习助手
扣子(coze.cn)是一款用来开发新一代 AI Chat Bot 的应用编辑平台,无论你是否有编程基础,都可以通过这个平台来快速创建各种类型的 Chat Bot,并将其发布到各类社交平台和通讯软件上 ...
- magic book
magic book.md body { font-family: var(--vscode-markdown-font-family, -apple-system, BlinkMacSystemFo ...
- 八: Mysql配置文件的使用
# Mysql配置文件的使用 1. 配置文件格式 与在命令行中指定启动选项不同的是,配置文件中的启动选项被划分为若干个组,丽个组有一个组名, 用中括号 [ ]扩起来,像这样: 像这个配置文件里就定义了 ...