Azure WebJobs和线程安全性

4

我初次接触Azure WebJobs,有一个基本的问题。我将控制台应用程序部署为WebJob,由于控制台应用程序通常会频繁使用static关键字,因此我有一些本地静态变量如下所示,我是否会遇到WebJob同时更新这些变量的多个线程而出现问题?

class Program
{
    private static MyService _service;
    private static int _counter;

    static void Main()
    {
      ...
    }

    private static void Method1() { .... }
}

2
这似乎与WebJob无关,只是一个关于静态变量线程安全的基本问题。 - Amit Apple
2
"通常控制台应用程序会大量使用静态变量" --- 我的不会。仅仅因为入口点必须是静态的,并不意味着整个应用程序都必须成为并发的噩梦。 - Ronnie Overby
6个回答

4

由于您在问题中标记了azure-webjobs-sdk,我记得您几天前有另一个关于SDK的问题,因此我会假设您的webjob使用了SDK,并且多线程是由于我们并行运行触发函数引起的。

我的答案的第一部分不一定与web jobs相关:

class Program
{
    private static MyService _service;
    private static int _counter;

    private static readonly object _lock = new object();

    // The 3 blocks of code are thread safe by themselves
    // but put together, there is no guarantee. Avoid
    // multiple locks if you can
    private static void Method1() 
    {
        // 1. You can use a lock 
        lock(_lock)
        {
            // All the code here will be executed by a single thread. 
            // If another thread tries to get the lock, it will have
            // to wait until the lock is released

            _service.DoSomething();
        }

        // 2. For atomic operations you can use Interlocked
        Interlocked.Increment(ref _counter);

        // 3. For conditional locking
        if (_service.SomeBooleanProperty)
        {
            lock(_lock)
            {
                // Check again to see the condition still holds
                // because it might have changed between the
                // execution of the first if and the lock
                if (_service.SomeBooleanProperty)
                {
                    // Execute the non thread safe code
                }
            }
        }
    }
}

参考Interlocked类。

现在我要谈论WebJobs SDK:

如果你想控制处理并发,请设置Queue.BatchSize属性。默认值为16。如果将其设置为1,则所有内容都按顺序运行。请参见此处的参考资料。如果您在多个实例并发(同一作业的多个实例)方面遇到并发问题,则应使用blob leases。


谢谢Victor,我想确认一下,当我使用SDK并将Queue.BatchSize设置为1以外的值时,WebJobs会启动多个线程来执行我的代码吗?所以我应该使用租赁blob或将BatchSize设置为1来请求WebJobs按顺序运行?如果我不使用SDK,我仍然应该使用锁来保护我的本地变量,比如_service吗?你有示例代码可以展示如何使用租赁blob吗?谢谢。 - Ray

2

如果您想在Azure WebJob上下文中将批处理大小限制为一个,则可以执行以下操作:

static void Main()
        {
            InitializeStorage();
            JobHostConfiguration config = new JobHostConfiguration();
            config.Queues.BatchSize = 1; // the Azure default is 16
            var host = new JobHost(config);
            host.RunAndBlock();
        }

阅读本文中与队列并行执行相关的章节,了解更多信息。


这正是我一直在寻找的 - 我们遇到了一个订购问题,将BatchSize设置为1证明了这是我们处理器的问题。现在要想办法修复它... - moswald

2
不会,除非你在控制台应用程序中专门编写了多线程代码。当在Azure中调用WebJob时,它将获得自己的进程。例如,这是一个名为ConsoleTestApp.exe的WebJob的进程资源管理器屏幕截图。请注意,它的PID与您的网站运行的W3WP进程分开。

1
多个作业实例将在不同的线程中运行,在同一应用程序域中。这意味着静态变量不是线程安全的。
这很容易测试。这也是为什么如果您向System.Diagnostics.Trace.Listeners注册一个TextWriterTraceListener,然后从多个作业使用Trace.WriteLine写入它,并最终将其删除,您将会得到交叉对话的原因。
自然而然地,控制台应用程序经常使用静态内容。
控制台应用程序并不本质上被迫使用静态变量,WebJobs也是如此。创建一个类的实例来进行一些处理,并调用该类的方法。以下是一个非常基本的示例:
class Program
{
    public int Start(string[] args)
    {
        // You can use non-static variables here
        Console.WriteLine("There were {0} arguments.", args.Length);
        return 0;
    }

    static int Main(string[] args)
    {
        return new Program().Start(args);
    }
}

1
你所说的“线程”是什么意思?
是指你的代码生成的一些实际线程吗?(在你的示例代码中没有展示任何内容。)如果是这样,那么当你访问静态变量时,确实会遇到问题。就像任何其他多线程代码一样。
但我假设,你实际上并不是指线程,而是指作业的单独运行实例。这些是独立的进程,因此每个进程都有自己的静态变量副本。它们甚至无法访问彼此的内存。
请注意,WebJob只是一个应用程序,与其他应用程序一样,并没有什么魔法。如果多次运行相同的应用程序,则每个实例都有为静态变量分配的自己的内存。

只有在网站进行扩展时,WebJob 才会出现单独的实例。 - Matthew

0
我们需要看到WebJobs的处理过程,类似于ASP.Net请求处理模型。就像在ASP.Net中每个请求都在不同的线程中处理一样,在WebJobs SDK中每个作业的调用都会在不同的线程中发生。如果您有被Web Jobs操作的静态变量,可能会出现问题。如果您的应用程序中只有一个WebJob,则可以通过将batchsize设置为1来避免问题,以便一次只运行一个作业,并且它可以操作静态变量。
但是,如果有多个Web Job函数并且所有这些函数都使用相同的静态变量,则批处理大小无法帮助,因为所有类型中至少有一个调用将同时运行,并且所有这些调用都操作相同的静态变量。
我们在使用静态变量和存在内存泄漏的旧代码库中也遇到了同样的问题。我们计划生成另一个exe来从webjobs exe中进行实际处理。子exe仅接受WebJob exe所需的作业参数,并在完成单个作业后关闭自身。这样,旧代码中的内存泄漏和静态变量就不会造成任何麻烦。WebJobs exe可以简单地监听来自子exe的console.writes并写入相同的流,以便在WebJobs门户中可见。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接