草庐IT

.NET Core 多线程的用法,以及用例

雨太阳 2023-03-28 原文

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 多线程的用法,以及用例的更多相关文章

  1. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  2. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

    我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

  3. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

  4. 阿里云国际版免费试用:如何注册以及注意事项 - 2

    作为新的阿里云用户,您可以50免费试用多种优惠,价值高达1,700美元(或8,500美元)。这将让您了解和体验阿里云平台上提供的一系列产品和服务。如果您以个人身份注册免费试用,您将获得价值1,700美元的优惠。但是,如果您是注册公司,您可以选择企业免费试用,提交基本信息通过企业实名注册验证,即可开始价值$8,500的免费试用!本教程介绍了如何设置您的帐户并使用您的免费试用版。​关于免费试用在我们开始此试用之前,您还必须遵守以下条款和条件才能访问您的免费试用:只有在一年内创建的账户才有资格获得阿里云免费试用。通过此免费试用优惠,用户可以免费试用免费试用活动页面上列出的每种产品一次。如果您有多个帐

  5. ruby - 如何让Ruby捕获线程中的语法错误 - 2

    我正在尝试使用ruby​​编写一个双线程客户端,一个线程从套接字读取数据并将其打印出来,另一个线程读取本地数据并将其发送到远程服务器。我发现的问题是Ruby似乎无法捕获线程内的错误,这是一个示例:#!/usr/bin/rubyThread.new{loop{$stdout.puts"hi"abc.putsefsleep1}}loop{sleep1}显然,如果我在线程外键入abc.putsef,代码将永远不会运行,因为Ruby将报告“undefinedvariableabc”。但是,如果它在一个线程内,则没有错误报告。我的问题是,如何让Ruby捕获这样的错误?或者至少,报告线程中的错误?

  6. ruby - 如何在 ruby​​ 中运行后台线程? - 2

    我是ruby​​的新手,我认为重新构建一个我用C#编写的简单聊天程序是个好主意。我正在使用Ruby2.0.0MRI(Matz的Ruby实现)。问题是我想在服务器运行时为简单的服务器命令提供I/O。这是从示例中获取的服务器。我添加了使用gets()获取输入的命令方法。我希望此方法在后台作为线程运行,但该线程正在阻塞另一个线程。require'socket'#Getsocketsfromstdlibserver=TCPServer.open(2000)#Sockettolistenonport2000defcommandsx=1whilex==1exitProgram=gets.chomp

  7. ruby - Rails 开发服务器、PDFKit 和多线程 - 2

    我有一个使用PDFKit呈现网页的pdf版本的Rails应用程序。我使用Thin作为开发服务器。问题是当我处于开发模式时。当我使用“bundleexecrailss”启动我的服务器并尝试呈现任何PDF时,整个过程会陷入僵局,因为当您呈现PDF时,会向服务器请求一些额外的资源,如图像和css,看起来只有一个线程.如何配置Rails开发服务器以运行多个工作线程?非常感谢。 最佳答案 我找到的最简单的解决方案是unicorn.geminstallunicorn创建一个unicorn.conf:worker_processes3然后使用它:

  8. ruby - Ruby 1.9.1 中的 native 线程,对我有什么好处? - 2

    所以,Ruby1.9.1现在是declaredstable.Rails应该与它一起工作,并且正在慢慢地将gem移植到它。它具有native线程和全局解释器锁(GIL)。自从GIL到位后,原生线程是否比1.9.1中的绿色线程有任何优势? 最佳答案 1.9中的线程是原生的,但它们被“放慢了速度”,一次只允许一个线程运行。这是因为如果线程真的并行运行,它会混淆现有代码。优点:IO现在在线程中是异步的。如果一个线程阻塞在IO上,那么另一个线程将继续执行直到IO完成。C扩展可以使用真正的线程。缺点:任何非线程安全的C扩展都可能存在使用Thre

  9. ruby - ruby 中的同一个程序如何接受来自用户的输入以及命令行参数 - 2

    我的ruby​​脚本从命令行参数获取某些输入。它检查是否缺少任何命令行参数,然后提示用户输入。但是我无法使用gets从用户那里获得输入。示例代码:test.rbname=""ARGV.eachdo|a|ifa.include?('-n')name=aputs"Argument:#{a}"endendifname==""puts"entername:"name=getsputsnameend运行脚本:rubytest.rbraghav-k错误结果:test.rb:6:in`gets':Nosuchfileordirectory-raghav-k(Errno::ENOENT)fromtes

  10. ruby - 有人可以解释一下在 Ruby 中注入(inject)的真实、通俗易懂的用法吗? - 2

    我正在学习Ruby,遇到了inject。我正处于理解它的风口浪尖,但当我是那种需要真实世界的例子来学习一些东西的人时。我遇到的最常见的例子是人们使用inject来添加一个(1..10)范围的总和,我不太关心这个。这是一个任意的例子。在实际程序中我会用它做什么?我正在学习,所以我可以继续使用Rails,但我不必有一个以Web为中心的示例。我只需要一些我可以全神贯注的目标。谢谢大家。 最佳答案 inject有时可以通过它的“其他”名称reduce更好地理解。它是一个对Enumerable进行操作(迭代一次)并返回单个值的函数。它有许多有

随机推荐