1

摘要:我正在尝试查看是否可以重构一些具有常规模式的 C++ 代码,以使其更易于更新和维护。

详情

我有一些代码可以创建线程本地计数器以在程序执行期间跟踪统计信息。目前,当向源代码添加统计信息时,需要更新 5 项内容:计数器线程本地声明、计数器总数声明、重置线程计数器的函数、将线程计数器添加到总数的函数、和打印功能。

代码如下所示:

// Adding a statistic named 'counter'

// Declaration of counter
__thread int counter = 0;
int total_counter = 0;

// In reset function
counter = 0;

// In add function
total_counter += counter;

// In print function
printf("counter value is: %d\n", total_counter);

我可以看到如何为计数器的声明创建一个宏,如下所示:

#define STAT(name) __thread int name; \
                   int total_##name;

但我还没有想过如何扩展它来更新addandreset函数。理想情况下,我想输入类似STAT(counter)的内容并处理所有用于管理统计信息的声明和功能。

编辑

我已经有用于更新代码中的统计信息的宏。诸如此类的东西STAT_INC(counter)会增加本地计数器的值。然后,当线程完成执行时,它的线程本地值被添加到总体总数中。因此,每个统计数据的名称都很重要,这就是为什么数组对我不起作用的原因。因为真正的计数器名称是cache_hit比这些更有意义的东西counter[2],我不想失去为所创建的统计信息指定任意名称的能力。如果可能的话,只是为了简化我在声明统计信息时必须编写的代码量。

4

3 回答 3

1

这或多或少地将您在问题中描述的内容封装在模板类中:

enum StatNames {
    STAT_rx_bytes,
    STAT_tx_bytes,
    //...,
};

template <StatNames SN>
class Stat {
    static const char *name_;
    static __thread int x_;
    static int total_;

public:
    Stat(const char *name) { name_ = name; }
    static void reset () { x_ = 0; }
    static void add () { total_ += x_; }
    static void print () {
        std::cout << name_ << " value is: " << total_ << "\n";
    }
    static int & x () { return x_; }
    static int total () { return total_; }
};

template <StatNames SN> const char * Stat<SN>::name_;
template <StatNames SN> __thread int Stat<SN>::x_;
template <StatNames SN> int Stat<SN>::total_;

#define STAT(name) Stat<STAT_##name> name(#name)

然后,您可以编写如下代码:

STAT(rx_bytes);

void * test (void *)
{
    rx_bytes.x() += 4;
    rx_bytes.add();
    std::cout << pthread_self() << ": " << rx_bytes.x() << "\n";
    return 0;
}

int main ()
{
    pthread_t t[2];
    pthread_create(&t[0], 0, test, 0);
    pthread_create(&t[1], 0, test, 0);
    pthread_join(t[0], 0);
    pthread_join(t[1], 0);
    rx_bytes.print();
}
于 2013-05-09T19:55:19.120 回答
1

(7分钟后无人接听……我收到礼物了!)

所以基本上你不想要五个单独的命名变量。您可以使用数组或向量:

int counters[5];

然后很容易更新某个计数器:

class Counter {
    int counters[5];
    void update_nth(int n)
    {
        counters[n]++;
    }
};

与所有其他变量类似。

于 2013-05-09T19:03:48.780 回答
1

跟进 H2CO3 的回答,有一个常见的成语如下所示:

enum CounterEnums {
    MyFirstCounter,
    MySecondCounter,
    // ... add new counter names here ...
    NumCounters
};
class Counter {
    int counters[NumCounters];
public:
    void update(int n) { counters[n]++; }
};

现在,您可以轻松添加另一个计数器,只需将其放在 NumCounters. 现在,您可以声明您的实例,例如:

Counter totals; // global
boost::thread_specific_pointer<Counter> counters; // per-thread

并使用

counters->update(MyFirstCounter);

(您还需要某种方法来更新totals和归零您的每线程计数器,但我会……将其作为练习留给读者)。

于 2013-05-09T19:18:58.190 回答