Create different handle of class in the macro based on number of times macro is called

I would not use a macro, at least you haven’t explained why a task or function would not work.

One basic problem with your macro is that it is just text expansion. So even though you have two lines of text in your fork join, it expands to

   fork
       utils_h = new(); 
       utils_h.cmd_queue = {"cmd1","cmd11","cmd111"};
       utils_h = new();
       utils_h.cmd_queue = {"cmd1","cmd11","cmd111"};
   join

Since these 4 statements could be executed in any order, you are lucky you didn’t get a null handle reference error. Putting begin/end around the statements as part of the macro will fix this, but you still have the problem of two threads writing to the same variable, utils_h. Also, there is no point in using a fork unless the statements within it have some time consuming process associated with them. It’s not really clear your reason for adding the commands in parallel if it’s the execution of command that need to be in parallel.

What you probably want is a queue of util classes. Instead of your macro, have a function that adds constructs a single util class, and the commands to the queue inside that object. Then either fork off processes for each set of commands.

class utils_helper_c;
  
  string cmd_queue[$];
  
  function new(string name = "utils_h");
    $display("creating %s object",name);
  endfunction : new
endclass : utils_helper_c

class test;
   utils_helper_c utils_q[$];
   function void add_cmds(string cmds[$]);
      utils_helper_c utils_h = new();
      utils_h.cmd_queue = cmds;
      utils_q.push_back(utils_h);
   endfunction : add_cmds
   task run();
      $display("running run task...");
      add_cmds({"cmd1","cmd11","cmd111"});
      add_cmds({"cmd2","cmd22","cmd222"});
      while(utils_q.size() !=0)  // or repeat wait(util_q.size() !=0) 
	fork
	   utils_helper_c utils_h = utils_q.pop_front();
	   foreach(utils_h.cmd_queue[i])
	     #5
		$display("%t printing cmds %s",$time,utils_h.cmd_queue[i]);
	 join_none
   endtask : run
   
endclass : test

module top;
  
  test t;
  initial begin
     t = new();
     t.run();
  end
   
endmodule : top